/** * @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 InsertOperation from '@ckeditor/ckeditor5-engine/src/model/operation/insertoperation'; import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; import ShiftEnter from '../src/shiftenter'; describe( 'ShiftEnterCommand', () => { let editor, model, doc, schema, command; beforeEach( () => { return ModelTestEditor.create( { plugins: [ ShiftEnter ] } ) .then( newEditor => { editor = newEditor; model = editor.model; doc = model.document; command = editor.commands.get( 'shiftEnter' ); 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( 'ShiftEnterCommand', () => { it( 'soft breaks a block using parent batch', () => { setData( model, '
foo[]
' ); model.change( writer => { expect( writer.batch.operations ).to.length( 0 ); editor.execute( 'shiftEnter' ); expect( writer.batch.operations ).to.length.above( 0 ); } ); } ); it( 'creates InsertOperation if soft enter is at the beginning of block', () => { setData( model, '[]foo
' ); editor.execute( 'shiftEnter' ); const operations = Array.from( doc.history.getOperations() ); expect( operations[ operations.length - 1 ] ).to.be.instanceof( InsertOperation ); } ); it( 'creates InsertOperation if soft enter is at the end of block', () => { setData( model, 'foo[]
' ); editor.execute( 'shiftEnter' ); const operations = Array.from( doc.history.getOperations() ); expect( operations[ operations.length - 1 ] ).to.be.instanceof( InsertOperation ); } ); } ); describe( 'execute()', () => { describe( 'collapsed selection', () => { test( 'inserts in the root', 'foo[]bar', 'foox
foo[]bar
y
', 'x
foo
y
' ); test( 'inserts at the end of block', 'x
foo[]
y
', 'x
foo
y
' ); test( 'inserts at the beginning of block', 'x
[]foo
y
', 'x
y
' ); describe( 'copyOnEnter', () => { beforeEach( () => { schema.extend( '$text', { allowAttributes: [ 'foo', 'bar' ] } ); schema.setAttributeProperties( 'foo', { copyOnEnter: true } ); } ); test( 'allowed attributes are copied', '<$text foo="true">test[]$text>
', '<$text foo="true">test$text>
<$text bar="true">test[]$text>
', '<$text bar="true">test$text>
<$text bar="true" foo="true">test[]$text>
', '<$text bar="true" foo="true">test$text>
ab[cd]ef
ghi
', 'ab
ghi
' ); test( 'places selection in the 2nd element', 'd]ef
ghi
', '[]ef
ghi
' ); test( 'does nothing for selection that contains more than one range', '[abc]
[def]
', '[abc]
[def]
' ); test( 'inserts break in empty element after it was fully selected', 'x
[abcdef]
y
', 'x
y
' ); // See: comment in softBreakAction(). test( 'leaves one empty element after two were fully selected', '[abc
def]
', '[]
' ); test( 'should insert the break in inline limit element - collapsed', 'f]oo
' ); command.execute(); expect( getData( model ) ).to.equal( 'f]oo
' ); } ); } ); test( 'should break paragraph in blockLimit', 'foo[]bar
foo
[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 ); } ); } } ); describe( '#isEnabled', () => { it( 'should be disabled ifFoo.[]
' ); expect( command.isEnabled ).to.equal( true ); } ); it( 'should be enabled for collapsed selection in heading', () => { setData( model, 'Foo.[]
F[oo.]
[Foo.
Bar.]
' ); expect( command.isEnabled ).to.equal( true ); } ); it( 'should be enabled for non-collapsed selection in headings', () => { setData( model, 'F[oo
F[oo.
B]ar.
' ); // Refresh it manually because we're in the middle of a change block. command.refresh(); expect( command.isEnabled ).to.equal( false ); } ); } ); it( 'should be disabled for non-collapsed selection which ends in element inside a block limit element', () => { model.change( () => { setData( model, 'Fo[o.
Bar].
[x]
[foo]
' ); expect( command.isEnabled ).to.equal( false ); } ); it( 'should be disabled for multi-ranges selection (2)', () => { setData( model, '[]x
[]foo
' ); expect( command.isEnabled ).to.equal( false ); } ); } ); } );