/** * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ import TableKeyboard from '../src/tablekeyboard'; import Table from '../src/table'; import TableEditing from '../src/tableediting'; import TableSelection from '../src/tableselection'; import { modelTable } from './_utils/utils'; import { getTableCellsContainingSelection } from '../src/utils/selection'; import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import ImageEditing from '@ckeditor/ckeditor5-image/src/image/imageediting'; import MediaEmbedEditing from '@ckeditor/ckeditor5-media-embed/src/mediaembedediting'; import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; import Image from '@ckeditor/ckeditor5-image/src/image'; import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption'; import HorizontalLine from '@ckeditor/ckeditor5-horizontal-line/src/horizontalline'; import MediaEmbed from '@ckeditor/ckeditor5-media-embed/src/mediaembed'; import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard'; import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils'; import global from '@ckeditor/ckeditor5-utils/src/dom/global'; import env from '@ckeditor/ckeditor5-utils/src/env'; describe( 'TableKeyboard', () => { let editor, model, modelRoot, tableSelection, tableKeyboard, selection, editorElement; const imageUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAUCAQAAADRyVAeAAAAKklEQVR42u3PAQ0AAAwCI' + 'O0f+u/hoAHNZUJFRERERERERERERERERLYiD9N4FAFj2iK6AAAAAElFTkSuQmCC'; beforeEach( async () => { editorElement = global.document.createElement( 'div' ); global.document.body.appendChild( editorElement ); editor = await ClassicTestEditor.create( editorElement, { plugins: [ Table, Paragraph, Image, ImageCaption, HorizontalLine, MediaEmbed ] } ); model = editor.model; selection = model.document.selection; modelRoot = model.document.getRoot(); tableSelection = editor.plugins.get( TableSelection ); tableKeyboard = editor.plugins.get( TableKeyboard ); } ); afterEach( async () => { editorElement.remove(); await editor.destroy(); } ); it( 'should have pluginName', () => { expect( TableKeyboard.pluginName ).to.equal( 'TableKeyboard' ); } ); describe( 'Tab key handling', () => { let domEvtDataStub; beforeEach( () => { domEvtDataStub = { keyCode: getCode( 'Tab' ), preventDefault: sinon.spy(), stopPropagation: sinon.spy() }; } ); it( 'should do nothing if pressed other key', () => { setModelData( model, modelTable( [ [ '11', '12[]' ] ] ) ); domEvtDataStub.keyCode = getCode( 'a' ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.notCalled( domEvtDataStub.preventDefault ); sinon.assert.notCalled( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '11', '12[]' ] ] ) ); } ); it( 'should do nothing if Ctrl+Tab is pressed', () => { setModelData( model, modelTable( [ [ '11', '12[]' ] ] ) ); domEvtDataStub.ctrlKey = true; editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.notCalled( domEvtDataStub.preventDefault ); sinon.assert.notCalled( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '11', '12[]' ] ] ) ); } ); describe( 'on Tab key press', () => { it( 'should do nothing if the selection is not in a table', () => { setModelData( model, '[]' + modelTable( [ [ '11', '12' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.notCalled( domEvtDataStub.preventDefault ); sinon.assert.notCalled( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), '[]' + modelTable( [ [ '11', '12' ] ] ) ); } ); it( 'should move to the next cell', () => { setModelData( model, modelTable( [ [ '11[]', '12' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.calledOnce( domEvtDataStub.preventDefault ); sinon.assert.calledOnce( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '11', '[12]' ] ] ) ); } ); it( 'should create another row and move to the first cell in a new row', () => { setModelData( model, modelTable( [ [ '11', '[12]' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '11', '12' ], [ '[]', '' ] ] ) ); } ); it( 'should select the whole table if the "insertTableRowBelow" command is disabled', () => { setModelData( model, modelTable( [ [ '11', '12[]' ] ] ) ); const insertTableRowBelowCommand = editor.commands.get( 'insertTableRowBelow' ); insertTableRowBelowCommand.forceDisabled( 'test' ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); assertEqualMarkup( getModelData( model ), '[' + modelTable( [ [ '11', '12' ] ] ) + ']' ); } ); it( 'should move to the first cell of the next row if at the end of a row', () => { setModelData( model, modelTable( [ [ '11', '12[]' ], [ '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '11', '12' ], [ '[21]', '22' ] ] ) ); } ); it( 'should move to the next table cell if the block content is partially selected', () => { setModelData( model, modelTable( [ [ '11', '12[foo]bar', '13' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '11', '12foobar', '[13]' ] ] ) ); } ); it( 'should move to the next cell containing an image', () => { setModelData( model, modelTable( [ [ '11[]', 'foo' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.calledOnce( domEvtDataStub.preventDefault ); sinon.assert.calledOnce( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '11', '[foo]' ] ] ) ); } ); it( 'should move to the next cell containing a block quote', () => { model.schema.register( 'blockQuote', { allowWhere: '$block', allowContentOf: '$root' } ); editor.conversion.elementToElement( { model: 'blockQuote', view: 'blockquote' } ); setModelData( model, modelTable( [ [ '11[]', '
foo
' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.calledOnce( domEvtDataStub.preventDefault ); sinon.assert.calledOnce( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '11', '
[foo]
' ] ] ) ); } ); it( 'should listen with the lower priority than its children', () => { // Cancel TAB event. editor.keystrokes.set( 'Tab', ( data, cancel ) => cancel() ); setModelData( model, modelTable( [ [ '11[]', '12' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.calledOnce( domEvtDataStub.preventDefault ); sinon.assert.calledOnce( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '11[]', '12' ] ] ) ); } ); describe( 'on table widget selected', () => { beforeEach( () => { editor.model.schema.register( 'block', { allowWhere: '$block', allowContentOf: '$block', isObject: true } ); editor.conversion.elementToElement( { model: 'block', view: 'block' } ); } ); it( 'should move caret to the first table cell on TAB', () => { const spy = sinon.spy(); editor.keystrokes.set( 'Tab', spy, { priority: 'lowest' } ); setModelData( model, '[' + modelTable( [ [ '11', '12' ] ] ) + ']' ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.calledOnce( domEvtDataStub.preventDefault ); sinon.assert.calledOnce( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '[11]', '12' ] ] ) ); // Should cancel event - so no other tab handler is called. sinon.assert.notCalled( spy ); } ); it( 'shouldn\'t do anything on other blocks', () => { const spy = sinon.spy(); editor.editing.view.document.on( 'keydown', spy ); setModelData( model, '[foo]' ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.notCalled( domEvtDataStub.preventDefault ); sinon.assert.notCalled( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), '[foo]' ); // Should not cancel event. sinon.assert.calledOnce( spy ); } ); } ); } ); describe( 'on Shift+Tab key press', () => { beforeEach( () => { domEvtDataStub.shiftKey = true; } ); it( 'should do nothing if the selection is not in a table', () => { setModelData( model, '[]' + modelTable( [ [ '11', '12' ] ] ) ); domEvtDataStub.keyCode = getCode( 'Tab' ); domEvtDataStub.shiftKey = true; editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.notCalled( domEvtDataStub.preventDefault ); sinon.assert.notCalled( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), '[]' + modelTable( [ [ '11', '12' ] ] ) ); } ); it( 'should move to the previous cell', () => { setModelData( model, modelTable( [ [ '11', '12[]' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.calledOnce( domEvtDataStub.preventDefault ); sinon.assert.calledOnce( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '[11]', '12' ] ] ) ); } ); it( 'should select the whole table if the caret is in the first table cell', () => { setModelData( model, 'foo' + modelTable( [ [ '[]11', '12' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); assertEqualMarkup( getModelData( model ), 'foo[' + modelTable( [ [ '11', '12' ] ] ) + ']' ); } ); it( 'should move to the last cell of a previous row if at the beginning of a row', () => { setModelData( model, modelTable( [ [ '11', '12' ], [ '[]21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '11', '[12]' ], [ '21', '22' ] ] ) ); } ); it( 'should move to the previous table cell if the block content is partially selected', () => { setModelData( model, modelTable( [ [ '11', '12[foo]bar', '13' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '[11]', '12foobar', '13' ] ] ) ); } ); it( 'should move to the previous cell containing an image', () => { setModelData( model, modelTable( [ [ 'foo', 'bar[]' ] ] ) ); editor.editing.view.document.fire( 'keydown', domEvtDataStub ); sinon.assert.calledOnce( domEvtDataStub.preventDefault ); sinon.assert.calledOnce( domEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '[foo]', 'bar' ] ] ) ); } ); } ); } ); describe( 'Arrow keys handling', () => { let leftArrowDomEvtDataStub, rightArrowDomEvtDataStub, upArrowDomEvtDataStub, downArrowDomEvtDataStub; beforeEach( () => { leftArrowDomEvtDataStub = { keyCode: getCode( 'ArrowLeft' ), preventDefault: sinon.spy(), stopPropagation: sinon.spy(), domTarget: global.document.body }; rightArrowDomEvtDataStub = { keyCode: getCode( 'ArrowRight' ), preventDefault: sinon.spy(), stopPropagation: sinon.spy(), domTarget: global.document.body }; upArrowDomEvtDataStub = { keyCode: getCode( 'ArrowUp' ), preventDefault: sinon.spy(), stopPropagation: sinon.spy(), domTarget: global.document.body }; downArrowDomEvtDataStub = { keyCode: getCode( 'ArrowDown' ), preventDefault: sinon.spy(), stopPropagation: sinon.spy(), domTarget: global.document.body }; } ); it( 'should do nothing if pressed some non-arrow key', () => { setModelData( model, modelTable( [ [ '00', '01[]' ] ] ) ); leftArrowDomEvtDataStub.keyCode = getCode( 'a' ); editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.notCalled( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]' ] ] ) ); } ); it( 'should do nothing if the selection is not in a table', () => { const modelData = '[]foobar' + modelTable( [ [ '00', '01' ] ] ); setModelData( model, modelData ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.notCalled( upArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelData ); } ); describe( '#_navigateFromCellInDirection (finding a proper cell to move the selection to)', () => { describe( 'with no col/row-spanned cells', () => { beforeEach( () => { setModelData( model, 'foo' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + 'bar' ); } ); describe( 'from the first table cell', () => { let tableCell; beforeEach( () => { tableCell = modelRoot.getNodeByPath( [ 1, 0, 0 ] ); } ); it( 'should navigate to the start position of the cell on the right when the direction is "right"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'right' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '[]01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + 'bar' ); } ); it( 'should navigate to the start position of the cell below when the direction is "down"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'down' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '01', '02' ], [ '[]10', '11', '12' ], [ '20', '21', '22' ] ] ) + 'bar' ); } ); it( 'should select a whole table when the direction is "up"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'up' ); assertEqualMarkup( getModelData( model ), 'foo[' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + ']bar' ); } ); it( 'should select a whole table when the direction is "left"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'left' ); assertEqualMarkup( getModelData( model ), 'foo[' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + ']bar' ); } ); } ); describe( 'from the last table cell', () => { let tableCell; beforeEach( () => { tableCell = modelRoot.getNodeByPath( [ 1, 2, 2 ] ); } ); it( 'should navigate to the end position of the cell on the left when the direction is "left"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'left' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21[]', '22' ] ] ) + 'bar' ); } ); it( 'should navigate to the end position of the cell above when the direction is "up"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'up' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12[]' ], [ '20', '21', '22' ] ] ) + 'bar' ); } ); it( 'should select a whole table when the direction is "down"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'down' ); assertEqualMarkup( getModelData( model ), 'foo[' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + ']bar' ); } ); it( 'should select a whole table when the direction is "right"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'right' ); assertEqualMarkup( getModelData( model ), 'foo[' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + ']bar' ); } ); } ); describe( 'from a cell in the first column (but not first row)', () => { let tableCell; beforeEach( () => { tableCell = modelRoot.getNodeByPath( [ 1, 1, 0 ] ); } ); it( 'should navigate to start position of the cell on the right when the direction is "right"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'right' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '01', '02' ], [ '10', '[]11', '12' ], [ '20', '21', '22' ] ] ) + 'bar' ); } ); it( 'should navigate to the end position of the cell above when the direction is "up"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'up' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00[]', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + 'bar' ); } ); it( 'should navigate to the start position of the cell below when the direction is "down"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'down' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '[]20', '21', '22' ] ] ) + 'bar' ); } ); it( 'should navigate to the end position of the last cell in the previous row when the direction is "left"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'left' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '01', '02[]' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + 'bar' ); } ); } ); describe( 'from a cell in the last column (but not the last row)', () => { let tableCell; beforeEach( () => { tableCell = modelRoot.getNodeByPath( [ 1, 1, 2 ] ); } ); it( 'should navigate to the end position of the cell on the left when the direction is "left"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'left' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '01', '02' ], [ '10', '11[]', '12' ], [ '20', '21', '22' ] ] ) + 'bar' ); } ); it( 'should navigate to the end position the cell above when the direction is "up"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'up' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '01', '02[]' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + 'bar' ); } ); it( 'should navigate to the start position of the cell below when the direction is "down"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'down' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '[]22' ] ] ) + 'bar' ); } ); it( 'should navigate to the start position of the first cell in the next row when the direction is "right"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'right' ); assertEqualMarkup( getModelData( model ), 'foo' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '[]20', '21', '22' ] ] ) + 'bar' ); } ); } ); } ); describe( 'with col/row-spanned cells', () => { beforeEach( () => { // +----+----+----+----+----+ // | 00 | 01 | 02 | 03 | 04 | // +----+----+----+----+----+ // | 10 | 11 | 13 | 14 | // +----+ + +----+ // | 20 | | | 24 | // +----+----+----+----+----+ // | 30 | 31 | 33 | 34 | // +----+----+----+----+----+ // | 40 | 41 | 42 | 43 | 44 | // +----+----+----+----+----+ setModelData( model, modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); describe( 'when navigating to the right', () => { it( 'should navigate to the row-col-spanned cell when approaching from the upper-spanned row', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 0 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'right' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '[]11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the row-col-spanned cell when approaching from the lower-spanned row', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 2, 0 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'right' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '[]11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the row-spanned cell when approaching from the other row-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 1 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'right' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '[]13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the cell in the upper-spanned row when approaching from the row-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 2 ] ); // Cell 13. tableKeyboard._navigateFromCellInDirection( tableCell, 'right' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '[]14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the col-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 3, 0 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'right' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '[]31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate from the col-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 3, 1 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'right' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '[]33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); } ); describe( 'when navigating to the left', () => { it( 'should navigate to the row-spanned cell when approaching from the upper-spanned row', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 3 ] ); // Cell 14. tableKeyboard._navigateFromCellInDirection( tableCell, 'left' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13[]', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the row-spanned cell when approaching from the lower-spanned row', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 2, 1 ] ); // Cell 24. tableKeyboard._navigateFromCellInDirection( tableCell, 'left' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13[]', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the row-spanned cell when approaching from the other row-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 2 ] ); // Cell 13. tableKeyboard._navigateFromCellInDirection( tableCell, 'left' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11[]', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the cell in the upper-spanned row when approaching from the row-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 1 ] ); // Cell 11. tableKeyboard._navigateFromCellInDirection( tableCell, 'left' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10[]', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the col-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 3, 2 ] ); // Cell 33. tableKeyboard._navigateFromCellInDirection( tableCell, 'left' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31[]', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate from the col-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 3, 1 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'left' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30[]', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); } ); describe( 'when navigating down', () => { it( 'should navigate to the row-col-spanned cell when approaching from the first spanned column', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 0, 1 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'down' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '[]11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the row-col-spanned cell when approaching from the last spanned column', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 0, 2 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'down' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '[]11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the row-spanned cell when approaching from the other col-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 1 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'down' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '[]31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the cell in the first spanned column when approaching from the col-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 1 ] ); // Cell 11. tableKeyboard._navigateFromCellInDirection( tableCell, 'down' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '[]31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the row-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 0, 3 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'down' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '[]13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate from the row-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 2 ] ); // Cell 13. tableKeyboard._navigateFromCellInDirection( tableCell, 'down' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '[]33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); } ); describe( 'when navigating up', () => { it( 'should navigate to the col-spanned cell when approaching from the first spanned column', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 4, 1 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'up' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31[]', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the col-spanned cell when approaching from the last spanned column', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 4, 2 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'up' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31[]', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the row-col-spanned cell when approaching from the other col-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 3, 1 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'up' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11[]', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the cell in the first spanned column when approaching from the col-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 1 ] ); tableKeyboard._navigateFromCellInDirection( tableCell, 'up' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate to the row-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 3, 2 ] ); // Cell 33. tableKeyboard._navigateFromCellInDirection( tableCell, 'up' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13[]', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); it( 'should navigate from the row-spanned cell', () => { const tableCell = modelRoot.getNodeByPath( [ 0, 1, 2 ] ); // Cell 13. tableKeyboard._navigateFromCellInDirection( tableCell, 'up' ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03[]', '04' ], [ '10', { contents: '11', colspan: 2, rowspan: 2 }, { contents: '13', rowspan: 2 }, '14' ], [ '20', '24' ], [ '30', { contents: '31', colspan: 2 }, '33', '34' ], [ '40', '41', '42', '43', '44' ] ] ) ); } ); } ); } ); describe( 'when expanding selection', () => { beforeEach( () => { setModelData( model, modelTable( [ [ '00[]', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) ); } ); describe( 'from the first table cell', () => { let tableCell; beforeEach( () => { tableCell = getTableCellsContainingSelection( selection )[ 0 ]; } ); it( 'should expand the selection to the cell on the right when the direction is "right"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'right', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 0, 1 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell below when the direction is "down"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'down', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 1, 0 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should select a whole table when the direction is "up"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'up', true ); assertEqualMarkup( getModelData( model ), '[' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + ']' ); } ); it( 'should select a whole table when the direction is "left"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'left', true ); assertEqualMarkup( getModelData( model ), '[' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + ']' ); } ); } ); describe( 'from the last table cell', () => { let tableCell; beforeEach( () => { tableCell = modelRoot.getNodeByPath( [ 0, 2, 2 ] ); tableSelection.setCellSelection( tableCell, tableCell ); } ); it( 'should expand the selection to the cell on the left when the direction is "left"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'left', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 2, 1 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell above when the direction is "up"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'up', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 1, 2 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should select a whole table when the direction is "down"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'down', true ); assertEqualMarkup( getModelData( model ), '[' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + ']' ); } ); it( 'should select a whole table when the direction is "right"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'right', true ); assertEqualMarkup( getModelData( model ), '[' + modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) + ']' ); } ); } ); describe( 'from a cell in the first column (but not first row)', () => { let tableCell; beforeEach( () => { tableCell = modelRoot.getNodeByPath( [ 0, 1, 0 ] ); tableSelection.setCellSelection( tableCell, tableCell ); } ); it( 'should expand the selection to the cell on the right when the direction is "right"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'right', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 1, 1 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell above when the direction is "up"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'up', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 0, 0 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell below when the direction is "down"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'down', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 2, 0 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell above when the direction is "left"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'left', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 0, 0 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); } ); describe( 'from a cell in the last column (but not the last row)', () => { let tableCell; beforeEach( () => { tableCell = modelRoot.getNodeByPath( [ 0, 1, 2 ] ); tableSelection.setCellSelection( tableCell, tableCell ); } ); it( 'should expand the selection to the cell on the left when the direction is "left"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'left', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 1, 1 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell above when the direction is "up"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'up', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 0, 2 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell below when the direction is "down"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'down', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 2, 2 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell below when the direction is "right"', () => { tableKeyboard._navigateFromCellInDirection( tableCell, 'right', true ); expect( tableSelection.getAnchorCell() ).to.equal( tableCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 2, 2 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); } ); } ); } ); describe( 'with the table cells selected from outside', () => { describe( 'on a single table cell selected', () => { let anchorCell, focusCell; beforeEach( () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) ); anchorCell = focusCell = modelRoot.getNodeByPath( [ 0, 1, 1 ] ); tableSelection.setCellSelection( anchorCell, focusCell ); } ); describe( 'without shift pressed', () => { it( 'should move to the cell on the left', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10[]', '11', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should move to the cell on the right', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '11', '[]12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should move to the cell above the selection', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should move to the cell below the selection', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '[]21', '22' ] ] ) ); } ); } ); describe( 'with shift pressed', () => { beforeEach( () => { leftArrowDomEvtDataStub.shiftKey = true; rightArrowDomEvtDataStub.shiftKey = true; upArrowDomEvtDataStub.shiftKey = true; downArrowDomEvtDataStub.shiftKey = true; } ); it( 'should move to the cell below if TableSelection plugin is disabled', () => { editor.plugins.get( 'TableSelection' ).forceDisabled(); editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10[]', '11', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '[]20', '21', '22' ] ] ) ); expect( tableSelection.getAnchorCell() ).to.be.null; expect( tableSelection.getFocusCell() ).to.be.null; expect( selection.rangeCount ).to.equal( 1 ); } ); it( 'should expand the selection to the cell on the left', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 1, 0 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell on the right', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 1, 2 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell above the selection', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 0, 1 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); it( 'should expand the selection to the cell below the selection', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 2, 1 ] ) ); expect( selection.rangeCount ).to.equal( 2 ); } ); } ); } ); describe( 'on multiple table cells selected vertically (the anchor cell above the focus cell)', () => { let anchorCell, focusCell; beforeEach( () => { setModelData( model, modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); anchorCell = modelRoot.getNodeByPath( [ 0, 1, 1 ] ); focusCell = modelRoot.getNodeByPath( [ 0, 2, 1 ] ); tableSelection.setCellSelection( anchorCell, focusCell ); } ); describe( 'without shift pressed', () => { it( 'should move to the cell on the top left of the selection', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10[]', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell on the bottom right of the selection', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '[]22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell above the selection', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell below the selection', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '[]31', '32', '33' ] ] ) ); } ); } ); describe( 'with shift pressed', () => { beforeEach( () => { leftArrowDomEvtDataStub.shiftKey = true; rightArrowDomEvtDataStub.shiftKey = true; upArrowDomEvtDataStub.shiftKey = true; downArrowDomEvtDataStub.shiftKey = true; } ); it( 'should expand the selection to the cell on the left from the focus cell', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 2, 0 ] ) ); expect( selection.rangeCount ).to.equal( 4 ); } ); it( 'should expand the selection to the cell on the right from the focus cell', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 2, 2 ] ) ); expect( selection.rangeCount ).to.equal( 4 ); } ); it( 'should shrink the selection to the anchor cell', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( anchorCell ); expect( selection.rangeCount ).to.equal( 1 ); } ); it( 'should expand the selection to the cell below the focus cell', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 3, 1 ] ) ); expect( selection.rangeCount ).to.equal( 3 ); } ); } ); } ); describe( 'on multiple table cells selected vertically (the anchor cell below the focus cell)', () => { let anchorCell, focusCell; beforeEach( () => { setModelData( model, modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); anchorCell = modelRoot.getNodeByPath( [ 0, 2, 1 ] ); focusCell = modelRoot.getNodeByPath( [ 0, 1, 1 ] ); tableSelection.setCellSelection( anchorCell, focusCell ); } ); describe( 'without shift pressed', () => { it( 'should move to the cell on the top left of the selection', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10[]', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell on the bottom right of the selection', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '[]22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell above the selection', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell below the selection', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '[]31', '32', '33' ] ] ) ); } ); } ); describe( 'with shift pressed', () => { beforeEach( () => { leftArrowDomEvtDataStub.shiftKey = true; rightArrowDomEvtDataStub.shiftKey = true; upArrowDomEvtDataStub.shiftKey = true; downArrowDomEvtDataStub.shiftKey = true; } ); it( 'should expand the selection to the cell on the left from the focus cell', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 1, 0 ] ) ); expect( selection.rangeCount ).to.equal( 4 ); } ); it( 'should expand the selection to the cell on the right from the focus cell', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 1, 2 ] ) ); expect( selection.rangeCount ).to.equal( 4 ); } ); it( 'should shrink the selection to the anchor cell', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( anchorCell ); expect( selection.rangeCount ).to.equal( 1 ); } ); it( 'should expand the selection to the cell below the focus cell', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 0, 1 ] ) ); expect( selection.rangeCount ).to.equal( 3 ); } ); } ); } ); describe( 'on multiple table cell selected horizontally (the anchor cell is to the left of the focus cell ', () => { let anchorCell, focusCell; beforeEach( () => { setModelData( model, modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); anchorCell = modelRoot.getNodeByPath( [ 0, 1, 1 ] ); focusCell = modelRoot.getNodeByPath( [ 0, 1, 2 ] ); tableSelection.setCellSelection( anchorCell, focusCell ); } ); describe( 'without shift pressed', () => { it( 'should move to the cell on the top left of the selection', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10[]', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell on the bottom right of the selection', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '[]13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell above the selection', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell below the selection', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '[]22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); } ); describe( 'with shift pressed', () => { beforeEach( () => { leftArrowDomEvtDataStub.shiftKey = true; rightArrowDomEvtDataStub.shiftKey = true; upArrowDomEvtDataStub.shiftKey = true; downArrowDomEvtDataStub.shiftKey = true; } ); it( 'should expand the selection to the cell above the focus cell', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 0, 2 ] ) ); expect( selection.rangeCount ).to.equal( 4 ); } ); it( 'should expand the selection to the cell below the focus cell', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 2, 2 ] ) ); expect( selection.rangeCount ).to.equal( 4 ); } ); it( 'should shrink the selection to the anchor cell', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( anchorCell ); expect( selection.rangeCount ).to.equal( 1 ); } ); it( 'should expand the selection to the cell on the right to the focus cell', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); expect( tableSelection.getAnchorCell() ).to.equal( anchorCell ); expect( tableSelection.getFocusCell() ).to.equal( modelRoot.getNodeByPath( [ 0, 1, 3 ] ) ); expect( selection.rangeCount ).to.equal( 3 ); } ); } ); } ); describe( 'on multiple table cell selected diagonally', () => { beforeEach( () => { setModelData( model, modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); tableSelection.setCellSelection( modelRoot.getNodeByPath( [ 0, 1, 1 ] ), modelRoot.getNodeByPath( [ 0, 2, 2 ] ) ); } ); it( 'should move to the cell on the top left of selection', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10[]', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell on the bottom right of selection', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '[]23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell above selection', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '32', '33' ] ] ) ); } ); it( 'should move to the cell below selection', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02', '03' ], [ '10', '11', '12', '13' ], [ '20', '21', '22', '23' ], [ '30', '31', '[]32', '33' ] ] ) ); } ); } ); } ); describe( 'with the selection inside a table cell', () => { describe( 'with the selection at the boundary of a cell', () => { describe( 'simple cell text content', () => { it( 'should navigate to the cell on the left', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '[]11', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10[]', '11', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell on the right', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '11[]', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '11', '[]12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell above', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '[]11', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell below', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '11[]', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '[]21', '22' ] ] ) ); } ); } ); describe( 'multiple paragraphs in the cell content', () => { it( 'should navigate to the cell on the left', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '[]11x', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10[]', '11x', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell on the right', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '11x[]', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '11x', '[]12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell above', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '[]11x', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02' ], [ '10', '11x', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell below', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '11x[]', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '11x', '12' ], [ '20', '[]21', '22' ] ] ) ); } ); } ); describe( 'image widget with caption as only cell content', () => { it( 'should navigate to the cell on the left', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `[]11`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10[]', `11`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell on the right', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `11[]`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `11`, '[]12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell above', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `[]11`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02' ], [ '10', `11`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell below', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `11[]`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `11`, '12' ], [ '20', '[]21', '22' ] ] ) ); } ); } ); describe( 'horizontal line as only cell content (widget without $text positions)', () => { beforeEach( () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '[]', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell on the left', () => { // Twice because of fake caret next to widget. editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.called( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.called( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10[]', '', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell on the right', () => { // Twice because of fake caret next to widget. editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.called( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.called( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '', '[]12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell above', () => { // Twice because of fake caret next to widget. editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.called( upArrowDomEvtDataStub.preventDefault ); sinon.assert.called( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02' ], [ '10', '', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell below', () => { // Twice because of fake caret next to widget. editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.called( downArrowDomEvtDataStub.preventDefault ); sinon.assert.called( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '', '12' ], [ '20', '[]21', '22' ] ] ) ); } ); } ); describe( 'two horizontal lines as only cell content (widget without $text positions)', () => { it( 'should navigate to the cell on the left', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '[]', '12' ], [ '20', '21', '22' ] ] ) ); // Twice because of fake caret next to widget. editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.called( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.called( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10[]', '', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell on the right', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '[]', '12' ], [ '20', '21', '22' ] ] ) ); // Twice because of fake caret next to widget. editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.called( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.called( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '', '[]12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell above', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '[]', '12' ], [ '20', '21', '22' ] ] ) ); // Twice because of fake caret next to widget. editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.called( upArrowDomEvtDataStub.preventDefault ); sinon.assert.called( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02' ], [ '10', '', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should navigate to the cell below', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '[]', '12' ], [ '20', '21', '22' ] ] ) ); // Twice because of fake caret next to widget. editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.called( downArrowDomEvtDataStub.preventDefault ); sinon.assert.called( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '', '12' ], [ '20', '[]21', '22' ] ] ) ); } ); } ); } ); describe( 'with selection not at the boundary of a cell', () => { let styleElement; beforeEach( async () => { styleElement = global.document.createElement( 'style' ); styleElement.type = 'text/css'; styleElement.appendChild( global.document.createTextNode( ` * { font-size: 12px !important; font-family: serif !important; margin: 0 !important; padding: 0 !important; border: 0 !important } td { width: 30px !important; } tr:nth-child(2) td:nth-child(2) { width: 300px !important; } ` ) ); global.document.querySelector( 'head' ).appendChild( styleElement ); // The editing view must be focused because otherwise in Chrome the DOM selection will not contain // any ranges and jumpOverUiElement will crash (for the right arrow when shift is pressed). editor.editing.view.focus(); } ); afterEach( async () => { styleElement.remove(); } ); describe( 'simple cell text content', () => { it( 'should not navigate to the cell on the left', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '1[]1', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.notCalled( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( leftArrowDomEvtDataStub.stopPropagation ); } ); it( 'should not navigate to the cell on the right', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '1[]1', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.notCalled( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( rightArrowDomEvtDataStub.stopPropagation ); } ); it( 'should not navigate to the cell above', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '1[]1', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '[]11', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not navigate to the cell below', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '1[]1', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '11[]', '12' ], [ '20', '21', '22' ] ] ) ); } ); describe( 'when shift key is pressed', () => { beforeEach( () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '1[]1', '12' ], [ '20', '21', '22' ] ] ) ); leftArrowDomEvtDataStub.shiftKey = true; rightArrowDomEvtDataStub.shiftKey = true; upArrowDomEvtDataStub.shiftKey = true; downArrowDomEvtDataStub.shiftKey = true; } ); it( 'should not prevent default browser behavior for the left arrow pressed with shift', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.notCalled( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( leftArrowDomEvtDataStub.stopPropagation ); } ); it( 'should not prevent default browser behavior for the right arrow pressed with shift', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.notCalled( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( rightArrowDomEvtDataStub.stopPropagation ); } ); it( 'should expand selection to the beginning of the cell content', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '[1]1', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should expand selection to the end of the cell content', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '1[1]', '12' ], [ '20', '21', '22' ] ] ) ); } ); } ); } ); describe( 'selection inside paragraph', () => { const text = new Array( 20 ).fill( 0 ).map( () => 'word' ).join( ' ' ); it( 'should not navigate if caret is in the middle line of a text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + '[] ' + text, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.notCalled( upArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( upArrowDomEvtDataStub.stopPropagation ); } ); it( 'should move caret to beginning of cell content if caret is in the first line of a text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', 'word[] word' + text, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '[]word word' + text, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should move caret to end of cell content if caret is in the last line of a text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + 'word[] word', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', text + 'word word[]', '12' ], [ '20', '21', '22' ] ] ) ); } ); describe( 'when shift key is pressed', () => { beforeEach( () => { upArrowDomEvtDataStub.shiftKey = true; downArrowDomEvtDataStub.shiftKey = true; } ); it( 'should not prevent default browser behavior for the up arrow in the middle lines of the cell text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + '[] ' + text, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.notCalled( upArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( upArrowDomEvtDataStub.stopPropagation ); } ); it( 'should not prevent default browser behavior for the down arrow in the middle lines of cell text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + '[] ' + text, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.notCalled( downArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( downArrowDomEvtDataStub.stopPropagation ); } ); it( 'should expand collapsed selection to the beginning of the cell content', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', 'word[] word' + text, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '[word] word' + text, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not prevent default browser behavior for shrinking selection (up arrow)', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', 'word [word]' + text, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.notCalled( upArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( upArrowDomEvtDataStub.stopPropagation ); } ); it( 'should expand not collapsed selection to the beginning of the cell content from the selection anchor', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', 'word [word]' + text, '12' ], [ '20', '21', '22' ] ] ), { lastRangeBackward: true } ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '[word word]' + text, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should expand collapsed selection to the end of the cell content', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + 'word[] word', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', text + 'word[ word]', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not prevent default browser behavior for shrinking selection (down arrow)', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + '[word] word', '12' ], [ '20', '21', '22' ] ] ), { lastRangeBackward: true } ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.notCalled( downArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( downArrowDomEvtDataStub.stopPropagation ); } ); it( 'should expand not collapsed selection to the end of the cell content from the selection anchor', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + '[word] word', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', text + '[word word]', '12' ], [ '20', '21', '22' ] ] ) ); } ); } ); } ); if ( !env.isGecko ) { // These tests fails on Travis. They work correctly when started on local machine. // Issue is probably related to text rendering and wrapping. describe( 'with selection in the wrap area', () => { const text = new Array( 10 ).fill( 0 ).map( () => 'word' ).join( ' ' ); it( 'should move the caret to end if the caret is after the last space in the line next to the last one', () => { // This is also first position in the last line. setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + ' []word word word', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', text + ' word word word[]', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should move the caret to end if ther caret is at the last space in the line next to last one', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + '[] word word word', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', text + ' word word word[]', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not move the caret if it\'s just before the last space in the line next to last one', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text.substring( 0, text.length - 1 ) + '[]d word word word', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.notCalled( downArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( downArrowDomEvtDataStub.stopPropagation ); } ); describe( 'when shift key is pressed', () => { beforeEach( () => { upArrowDomEvtDataStub.shiftKey = true; downArrowDomEvtDataStub.shiftKey = true; } ); it( 'should expand collapsed selection to the end of the cell content', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + '[] word word word', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', text + '[ word word word]', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should expand not collapsed selection to the end of the cell content from the selection anchor', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', text + '[ word] word word', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', text + '[ word word word]', '12' ], [ '20', '21', '22' ] ] ) ); } ); } ); } ); } describe( 'with multiple paragraphs of text', () => { const text = new Array( 100 ).fill( 0 ).map( () => 'word' ).join( ' ' ); it( 'should not navigate if caret is in the middle of a line of text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `${ text }[]${ text }foobar`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.notCalled( upArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( upArrowDomEvtDataStub.stopPropagation ); } ); it( 'should move the caret to the beginning of a cell content if the caret is in the first line of text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `word[]${ text }foobar`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `[]word${ text }foobar`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not move the caret to the end of a cell content if the caret is not in the last line of text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `${ text }word []wordfoobar`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.notCalled( downArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( downArrowDomEvtDataStub.stopPropagation ); } ); it( 'should move the caret to end of a cell content if the caret is in the last line of text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `foobar${ text }word []word`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `foobar${ text }word word[]`, '12' ], [ '20', '21', '22' ] ] ) ); } ); describe( 'when shift key is pressed', () => { beforeEach( () => { upArrowDomEvtDataStub.shiftKey = true; downArrowDomEvtDataStub.shiftKey = true; } ); it( 'should expand selection to the beginning of the cell content', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `word[] ${ text }${ text }`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `[word] ${ text }${ text }`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should expand selection to the end of the cell content', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `${ text }${ text } []word`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `${ text }${ text } [word]`, '12' ], [ '20', '21', '22' ] ] ) ); } ); } ); } ); describe( 'with horizontal line widget', () => { const text = new Array( 100 ).fill( 0 ).map( () => 'word' ).join( ' ' ); it( 'should not navigate if the caret is in the middle line of text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `${ text }[]${ text }`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.notCalled( upArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( upArrowDomEvtDataStub.stopPropagation ); } ); it( 'should move the caret to the beginning of cell content if the caret is in the first line of text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `word[] ${ text }`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `[]word ${ text }`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should move the caret to the end of cell content if the caret is in the last line of text', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `${ text } word []word`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `${ text } word word[]`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not move the caret to the end of cell content if widget is selected in middle of a cell content', () => { const paragraph = `${ text }`; const hr = ''; setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `${ paragraph }[${ hr }]${ paragraph }${ hr }`, '12' ], [ '20', '21', '22' ] ] ) ); // Note: Two keydowns are necessary because the first one is handled by the WidgetTypeAround plugin // to activate the "fake caret". editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `${ paragraph }${ hr }[]${ text }${ hr }`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not move the caret to the end of cell content if widget is next to the selection', () => { const paragraph = `${ text }`; const hr = ''; setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `${ paragraph }[]${ hr }`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `${ paragraph }[${ hr }]`, '12' ], [ '20', '21', '22' ] ] ) ); } ); describe( 'when shift key is pressed', () => { beforeEach( () => { upArrowDomEvtDataStub.shiftKey = true; downArrowDomEvtDataStub.shiftKey = true; } ); it( 'should expand selection to the beginning of the cell content', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', 'foo[]bar', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '[foo]bar', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should expand selection to the end of the cell content', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', 'foo[]bar', '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', 'foo[bar]', '12' ], [ '20', '21', '22' ] ] ) ); } ); } ); } ); describe( 'contains image widget with caption and selection inside the caption', () => { it( 'should not navigate to the cell on the left', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `foo[]11`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.notCalled( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( leftArrowDomEvtDataStub.stopPropagation ); } ); it( 'should not navigate to the cell on the right', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `11[]foo`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.notCalled( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( rightArrowDomEvtDataStub.stopPropagation ); } ); it( 'should not navigate to the cell above (only to closest limit boundary)', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `foo1[]1`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `foo[]11`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not navigate to the cell above (only to paragraph above)', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `foo[]11`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.notCalled( upArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( upArrowDomEvtDataStub.stopPropagation ); } ); it( 'should not navigate to the cell above but should put caret at first position of the image caption', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `1[]1foo`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `[]11foo`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not navigate to the cell below when inside the image caption', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `1[]1foo`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `11[]foo`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not navigate to the cell below when followed by a paragraph', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `11[]foo`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.notCalled( downArrowDomEvtDataStub.preventDefault ); sinon.assert.notCalled( downArrowDomEvtDataStub.stopPropagation ); } ); it( 'should navigate to the cell below if the caret on last position in the image caption', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `foo11[]`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `foo11`, '12' ], [ '20', '[]21', '22' ] ] ) ); } ); it( 'should not navigate to the cell above but should select the image widget without caption', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `[]foo`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `[]foo`, '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should not navigate to the cell below but should select the image widget without caption', () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', `foo[]`, '12' ], [ '20', '21', '22' ] ] ) ); editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', `foo[]`, '12' ], [ '20', '21', '22' ] ] ) ); } ); } ); } ); } ); describe( 'for right-to-left content language', () => { let editor, model, modelRoot, tableSelection; beforeEach( () => { return VirtualTestEditor .create( { plugins: [ TableEditing, TableKeyboard, TableSelection, Paragraph, ImageEditing, MediaEmbedEditing ], language: 'ar' } ) .then( newEditor => { editor = newEditor; model = editor.model; modelRoot = model.document.getRoot(); tableSelection = editor.plugins.get( TableSelection ); } ); } ); afterEach( () => { return editor.destroy(); } ); describe( 'with the table cell selected from outside', () => { beforeEach( () => { setModelData( model, modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) ); tableSelection.setCellSelection( modelRoot.getNodeByPath( [ 0, 1, 1 ] ), modelRoot.getNodeByPath( [ 0, 1, 1 ] ) ); } ); it( 'should move to the cell on the right (visually flipped by the browser)', () => { editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '11', '[]12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should move to the cell on the left (visually flipped by the browser)', () => { editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10[]', '11', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should move to the cell above the selection', () => { editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub ); sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01[]', '02' ], [ '10', '11', '12' ], [ '20', '21', '22' ] ] ) ); } ); it( 'should move to the cell below the selection', () => { editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub ); sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault ); sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation ); assertEqualMarkup( getModelData( model ), modelTable( [ [ '00', '01', '02' ], [ '10', '11', '12' ], [ '20', '[]21', '22' ] ] ) ); } ); } ); } ); } ); } );