/** * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; import EnterCommand from '../src/entercommand'; import InsertDelta from '@ckeditor/ckeditor5-engine/src/model/delta/insertdelta'; import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; describe( 'EnterCommand', () => { let editor, model, doc, schema, command; beforeEach( () => { return ModelTestEditor.create() .then( newEditor => { editor = newEditor; model = editor.model; doc = model.document; command = new EnterCommand( editor ); editor.commands.add( 'enter', command ); schema = model.schema; // Note: We could use real names like 'paragraph', but that would make test patterns too long. // Plus, this is actually a good test that the algorithm can be used for any model. schema.register( 'img', { allowWhere: '$block' } ); schema.register( 'p', { inheritAllFrom: '$block', allowIn: 'blockLimit' } ); schema.register( 'h', { inheritAllFrom: '$block' } ); schema.register( 'inlineLimit', { allowIn: 'p', isLimit: true } ); schema.register( 'blockLimit', { allowIn: '$root', isLimit: true } ); schema.extend( '$text', { allowIn: [ 'inlineLimit', '$root' ] } ); } ); } ); describe( 'EnterCommand', () => { it( 'enters a block using parent batch', () => { setData( model, '

foo[]

' ); model.change( writer => { expect( writer.batch.deltas ).to.length( 0 ); editor.execute( 'enter' ); expect( writer.batch.deltas ).to.length.above( 0 ); } ); } ); it( 'creates InsertDelta if enter is at the beginning of block', () => { setData( model, '

[]foo

' ); editor.execute( 'enter' ); const deltas = Array.from( doc.history.getDeltas() ); expect( deltas[ deltas.length - 1 ] ).to.be.instanceof( InsertDelta ); } ); it( 'creates InsertDelta if enter is at the end of block', () => { setData( model, '

foo[]

' ); editor.execute( 'enter' ); const deltas = Array.from( doc.history.getDeltas() ); expect( deltas[ deltas.length - 1 ] ).to.be.instanceof( InsertDelta ); } ); } ); describe( 'execute()', () => { describe( 'collapsed selection', () => { test( 'does nothing in the root', 'foo[]bar', 'foo[]bar' ); test( 'splits block', '

x

foo[]bar

y

', '

x

foo

[]bar

y

' ); test( 'splits block at the end', '

x

foo[]

y

', '

x

foo

[]

y

' ); test( 'splits block at the beginning', '

x

[]foo

y

', '

x

[]foo

y

' ); test( 'inserts new block after empty one', '

x

[]

y

', '

x

[]

y

' ); } ); describe( 'non-collapsed selection', () => { test( 'only deletes the content when directly in the root', 'fo[ob]ar', 'fo[]ar' ); test( 'deletes text and splits', '

ab[cd]ef

ghi

', '

ab

[]ef

ghi

' ); test( 'places selection in the 2nd element', 'ab[c

d]ef

ghi

', 'ab

[]ef

ghi

' ); test( 'leaves one empty element after one was fully selected', '

x

[abcdef]

y

', '

x

[]

y

' ); test( 'leaves one empty element after two were fully selected', '

[abc

def]

', '

[]

' ); test( 'should not break inline limit elements - collapsed', '

foo[]bar

', '

foo[]bar

' ); test( 'should not break inline limit elements', '

foo[bar]baz

', '

foo[]baz

' ); test( 'should not break inline limit elements - selection partially inside', '

ba[r

f]oo

', '

ba[r

f]oo

' ); test( 'should break paragraph in blockLimit', '

foo[]bar

', '

foo

[]bar

' ); it( 'leaves one empty element after two were fully selected (backward)', () => { setData( model, '

[abc

def]

' ); // @TODO: Add option for setting selection direction to model utils. doc.selection._lastRangeBackward = true; command.execute(); expect( getData( model ) ).to.equal( '

[]

' ); } ); it( 'uses DataController.deleteContent', () => { const spy = sinon.spy(); editor.model.on( 'deleteContent', spy ); setData( model, '

[x]

' ); command.execute(); expect( spy.calledOnce ).to.be.true; } ); } ); function test( title, input, output ) { it( title, () => { setData( model, input ); command.execute(); expect( getData( model ) ).to.equal( output ); } ); } } ); } );