/** * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; import DeleteCommand from '../src/deletecommand'; import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; describe( 'DeleteCommand', () => { let editor, doc; testUtils.createSinonSandbox(); beforeEach( () => { return ModelTestEditor.create() .then( newEditor => { editor = newEditor; doc = editor.document; const command = new DeleteCommand( editor, 'backward' ); editor.commands.add( 'delete', command ); doc.schema.registerItem( 'paragraph', '$block' ); doc.schema.registerItem( 'heading1', '$block' ); } ); } ); afterEach( () => { return editor.destroy(); } ); it( 'has direction', () => { const command = new DeleteCommand( editor, 'forward' ); expect( command ).to.have.property( 'direction', 'forward' ); } ); describe( 'execute()', () => { it( 'uses enqueueChanges', () => { setData( doc, 'foo[]bar' ); doc.enqueueChanges( () => { editor.execute( 'delete' ); // We expect that command is executed in enqueue changes block. Since we are already in // an enqueued block, the command execution will be postponed. Hence, no changes. expect( getData( doc ) ).to.equal( '

foo[]bar

' ); } ); // After all enqueued changes are done, the command execution is reflected. expect( getData( doc ) ).to.equal( '

fo[]bar

' ); } ); it( 'locks buffer when executing', () => { setData( doc, 'foo[]bar' ); const buffer = editor.commands.get( 'delete' )._buffer; const lockSpy = testUtils.sinon.spy( buffer, 'lock' ); const unlockSpy = testUtils.sinon.spy( buffer, 'unlock' ); editor.execute( 'delete' ); expect( lockSpy.calledOnce ).to.be.true; expect( unlockSpy.calledOnce ).to.be.true; } ); it( 'deletes previous character when selection is collapsed', () => { setData( doc, 'foo[]bar' ); editor.execute( 'delete' ); expect( getData( doc ) ).to.equal( 'fo[]bar' ); } ); it( 'deletes selection contents', () => { setData( doc, 'fo[ob]ar' ); editor.execute( 'delete' ); expect( getData( doc ) ).to.equal( 'fo[]ar' ); } ); it( 'merges elements', () => { setData( doc, 'foo[]bar' ); editor.execute( 'delete' ); expect( getData( doc ) ).to.equal( 'foo[]bar' ); } ); it( 'does not try to delete when selection is at the boundary', () => { const spy = sinon.spy(); editor.data.on( 'deleteContent', spy ); setData( doc, '[]foo' ); editor.execute( 'delete' ); expect( getData( doc ) ).to.equal( '[]foo' ); expect( spy.callCount ).to.equal( 0 ); } ); it( 'passes options to modifySelection', () => { const spy = sinon.spy(); editor.data.on( 'modifySelection', spy ); setData( doc, 'foo[]bar' ); editor.commands.get( 'delete' ).direction = 'forward'; editor.execute( 'delete', { unit: 'word' } ); expect( spy.callCount ).to.equal( 1 ); const modifyOpts = spy.args[ 0 ][ 1 ][ 1 ]; expect( modifyOpts ).to.have.property( 'direction', 'forward' ); expect( modifyOpts ).to.have.property( 'unit', 'word' ); } ); it( 'passes options to deleteContent #1', () => { const spy = sinon.spy(); editor.data.on( 'deleteContent', spy ); setData( doc, 'foo[]bar' ); editor.execute( 'delete' ); expect( spy.callCount ).to.equal( 1 ); const deleteOpts = spy.args[ 0 ][ 1 ][ 2 ]; expect( deleteOpts ).to.have.property( 'doNotResetEntireContent', true ); } ); it( 'passes options to deleteContent #2', () => { const spy = sinon.spy(); editor.data.on( 'deleteContent', spy ); setData( doc, '[foobar]' ); editor.execute( 'delete' ); expect( spy.callCount ).to.equal( 1 ); const deleteOpts = spy.args[ 0 ][ 1 ][ 2 ]; expect( deleteOpts ).to.have.property( 'doNotResetEntireContent', false ); } ); it( 'leaves an empty paragraph after removing the whole content from editor', () => { setData( doc, '[Header 1Some text.]' ); editor.execute( 'delete' ); expect( getData( doc ) ).to.equal( '[]' ); } ); it( 'leaves an empty paragraph after removing the whole content inside limit element', () => { doc.schema.registerItem( 'section', '$root' ); doc.schema.limits.add( 'section' ); doc.schema.allow( { name: 'section', inside: '$root' } ); setData( doc, 'Foo' + '
' + '[Header 1' + 'Some text.]' + '
' + 'Bar.' ); editor.execute( 'delete' ); expect( getData( doc ) ).to.equal( 'Foo' + '
' + '[]' + '
' + 'Bar.' ); } ); it( 'leaves an empty paragraph after removing another paragraph from block element', () => { doc.schema.registerItem( 'section', '$block' ); doc.schema.registerItem( 'blockQuote', '$block' ); doc.schema.limits.add( 'section' ); doc.schema.allow( { name: 'section', inside: '$root' } ); doc.schema.allow( { name: 'paragraph', inside: 'section' } ); doc.schema.allow( { name: 'blockQuote', inside: 'section' } ); doc.schema.allow( { name: 'paragraph', inside: 'blockQuote' } ); setData( doc, '
[]
' ); editor.execute( 'delete' ); expect( getData( doc ) ).to.equal( '
[]
' ); } ); it( 'leaves an empty paragraph after removing the whole content when root element was not added as Schema.limits', () => { doc.schema.limits.delete( '$root' ); setData( doc, '[]' ); editor.execute( 'delete' ); expect( getData( doc ) ).to.equal( '[]' ); } ); it( 'replaces an empty element with paragraph', () => { setData( doc, '[]' ); editor.execute( 'delete' ); expect( getData( doc ) ).to.equal( '[]' ); } ); it( 'does not replace an element when Backspace or Delete key is held', () => { setData( doc, 'Bar[]' ); for ( let sequence = 1; sequence < 10; ++sequence ) { editor.execute( 'delete', { sequence } ); } expect( getData( doc ) ).to.equal( '[]' ); } ); it( 'does not replace with paragraph in another paragraph already occurs in limit element', () => { setData( doc, '[]' ); const element = doc.getRoot().getNodeByPath( [ 0 ] ); editor.execute( 'delete' ); expect( element ).is.equal( doc.getRoot().getNodeByPath( [ 0 ] ) ); } ); it( 'does not replace an element if a paragraph is not allowed in current position', () => { doc.schema.disallow( { name: 'paragraph', inside: '$root' } ); setData( doc, '[]' ); editor.execute( 'delete' ); expect( getData( doc ) ).to.equal( '[]' ); } ); } ); } );