/** * @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 ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; import { setData as setModelData, getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; import RestrictedEditingModeNavigationCommand from '../src/restrictededitingmodenavigationcommand'; describe( 'RestrictedEditingModeNavigationCommand', () => { let editor, forwardCommand, backwardCommand, model; beforeEach( () => { return ModelTestEditor .create() .then( newEditor => { editor = newEditor; model = editor.model; forwardCommand = new RestrictedEditingModeNavigationCommand( editor, 'forward' ); backwardCommand = new RestrictedEditingModeNavigationCommand( editor, 'backward' ); model.schema.register( 'paragraph', { inheritAllFrom: '$block' } ); editor.model.schema.extend( '$text', { allowAttributes: [ 'restrictedEditingException' ] } ); } ); } ); afterEach( () => { forwardCommand.destroy(); backwardCommand.destroy(); return editor.destroy(); } ); describe( 'forward command', () => { describe( 'isEnabled', () => { describe( 'collapsed selection', () => { it( 'should be true when there is a marker after the selection position', () => { setModelData( model, '[]foo bar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // []foo bar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( forwardCommand.isEnabled ).to.be.true; } ); it( 'should be false when there is no marker after the selection position', () => { setModelData( model, 'foo bar baz[]' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo bar baz[] model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( forwardCommand.isEnabled ).to.be.false; } ); it( 'should be false when the selection position is at a marker start and there are no more markers', () => { setModelData( model, 'foo []bar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo []bar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( forwardCommand.isEnabled ).to.be.false; } ); it( 'should be false when the selection position is in a marker and there are no more markers', () => { setModelData( model, 'foo b[]ar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo b[]ar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( forwardCommand.isEnabled ).to.be.false; } ); it( 'should be false when the selection position is at a marker end and there are no more markers', () => { setModelData( model, 'foo bar[] baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo bar[] baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( forwardCommand.isEnabled ).to.be.false; } ); } ); describe( 'expanded selection', () => { it( 'should be true when there is a marker after the first selection position', () => { setModelData( model, '[fo]o bar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // [fo]o bar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( forwardCommand.isEnabled ).to.be.true; } ); it( 'should be true when the selection overlaps the marker but the start position is before it', () => { setModelData( model, '[foo ba]r baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // [foo ba]r baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( forwardCommand.isEnabled ).to.be.true; } ); it( 'should be false when the selection overlaps the marker but the start position is after it', () => { setModelData( model, 'foo ba[r baz]' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo ba[r baz] model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( forwardCommand.isEnabled ).to.be.false; } ); } ); } ); describe( 'execute()', () => { describe( 'collapsed selection', () => { it( 'should move to the next marker', () => { setModelData( model, '[]foo bar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // []foo bar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); // []foo bar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:2', { range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ), usingOperation: true, affectsData: true } ); } ); forwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo [bar] baz' ); forwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo bar [baz]' ); } ); it( 'should move to the next marker when at the end of adjacent one', () => { setModelData( model, 'foo[]bar' ); const fiirstParagraph = model.document.getRoot().getChild( 0 ); const secondParagraph = model.document.getRoot().getChild( 1 ); // foo[]bar model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRangeIn( fiirstParagraph ), usingOperation: true, affectsData: true } ); } ); // foo[]bar model.change( writer => { writer.addMarker( 'restrictedEditingException:2', { range: writer.createRangeIn( secondParagraph ), usingOperation: true, affectsData: true } ); } ); forwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo[bar]' ); } ); it( 'should move to the closest marker when created in a reverse order', () => { setModelData( model, '[]foo bar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // []foo bar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:2', { range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ), usingOperation: true, affectsData: true } ); } ); // []foo bar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); forwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo [bar] baz' ); forwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo bar [baz]' ); } ); } ); describe( 'expanded selection', () => { it( 'should move to the next marker when the selection end overlaps the marker', () => { setModelData( model, '[foo b]ar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // [foo b]ar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); // [foo b]ar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:2', { range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ), usingOperation: true, affectsData: true } ); } ); forwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo [bar] baz' ); forwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo bar [baz]' ); } ); it( 'should move to the next marker when the selection start overlaps the marker', () => { setModelData( model, 'foo b[ar b]az' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo b[ar b]az model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); // foo b[ar b]az model.change( writer => { writer.addMarker( 'restrictedEditingException:2', { range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ), usingOperation: true, affectsData: true } ); } ); forwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo bar [baz]' ); } ); } ); } ); } ); describe( 'backward command', () => { describe( 'isEnabled', () => { describe( 'collapsed selection', () => { it( 'should be true when there is a marker before the selection position', () => { setModelData( model, 'foo bar baz[]' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo bar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( backwardCommand.isEnabled ).to.be.true; } ); it( 'should be false when there is no marker before the selection position', () => { setModelData( model, '[]foo bar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // []foo bar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( backwardCommand.isEnabled ).to.be.false; } ); it( 'should be false when the selection position is at a marker end and there are no more markers', () => { setModelData( model, 'foo bar[] baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo bar[] baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( backwardCommand.isEnabled ).to.be.false; } ); it( 'should be false when the selection position is in a marker and there are no more markers', () => { setModelData( model, 'foo b[]ar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo b[]ar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( backwardCommand.isEnabled ).to.be.false; } ); it( 'should be false when the selection position is at a marker start and there are no more markers', () => { setModelData( model, 'foo []bar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo []bar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( backwardCommand.isEnabled ).to.be.false; } ); } ); describe( 'expanded selection', () => { it( 'should be true when there is a marker before the first selection position', () => { setModelData( model, 'foo bar b[az]' ); const paragraph = model.document.getRoot().getChild( 0 ); // [fo]o bar b[az] model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( backwardCommand.isEnabled ).to.be.true; } ); it( 'should be false when the selection overlaps the marker but the start position is after it', () => { setModelData( model, 'foo b[ar baz]' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo b[ar baz] model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( backwardCommand.isEnabled ).to.be.false; } ); it( 'should be false when the selection overlaps the marker but the after position is after it', () => { setModelData( model, '[foo b]ar baz' ); const paragraph = model.document.getRoot().getChild( 0 ); // [foo b]ar baz model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); expect( backwardCommand.isEnabled ).to.be.false; } ); } ); } ); describe( 'execute()', () => { describe( 'collapsed selection', () => { it( 'should move to the previous marker', () => { setModelData( model, 'foo bar baz[]' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo bar baz[] model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); // foo bar baz[] model.change( writer => { writer.addMarker( 'restrictedEditingException:2', { range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ), usingOperation: true, affectsData: true } ); } ); backwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo [bar] baz' ); } ); it( 'should move to the previous marker when at the beginning of adjacent one', () => { setModelData( model, 'foo[]bar' ); const fiirstParagraph = model.document.getRoot().getChild( 0 ); const secondParagraph = model.document.getRoot().getChild( 1 ); // foo[]bar model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRangeIn( fiirstParagraph ), usingOperation: true, affectsData: true } ); } ); // foo[]bar model.change( writer => { writer.addMarker( 'restrictedEditingException:2', { range: writer.createRangeIn( secondParagraph ), usingOperation: true, affectsData: true } ); } ); backwardCommand.execute(); expect( getModelData( model ) ).to.equal( '[foo]bar' ); } ); it( 'should move to the closest previous marker', () => { setModelData( model, 'foo bar baz qux[]' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo bar baz qux[] model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); // foo bar baz qux[] model.change( writer => { writer.addMarker( 'restrictedEditingException:2', { range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ), usingOperation: true, affectsData: true } ); } ); backwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo bar [baz] qux' ); backwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo [bar] baz qux' ); } ); it( 'should move to the closest previous marker when created in a reverse order', () => { setModelData( model, 'foo bar baz qux[]' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo bar baz qux[] model.change( writer => { writer.addMarker( 'restrictedEditingException:2', { range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ), usingOperation: true, affectsData: true } ); } ); // foo bar baz qux[] model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); backwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo bar [baz] qux' ); backwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo [bar] baz qux' ); } ); } ); describe( 'expanded selection', () => { it( 'should move to the previous marker when the selection end overlaps the marker', () => { setModelData( model, 'foo bar b[az]' ); const paragraph = model.document.getRoot().getChild( 0 ); // foo bar b[az] model.change( writer => { writer.addMarker( 'restrictedEditingException:1', { range: writer.createRange( writer.createPositionAt( paragraph, 4 ), writer.createPositionAt( paragraph, 7 ) ), usingOperation: true, affectsData: true } ); } ); // foo bar b[az] model.change( writer => { writer.addMarker( 'restrictedEditingException:2', { range: writer.createRange( writer.createPositionAt( paragraph, 8 ), writer.createPositionAt( paragraph, 11 ) ), usingOperation: true, affectsData: true } ); } ); backwardCommand.execute(); expect( getModelData( model ) ).to.equal( 'foo [bar] baz' ); } ); } ); } ); } ); } );