/** * @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 FontCommand from '../src/fontcommand'; import Command from '@ckeditor/ckeditor5-core/src/command'; import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor'; import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; describe( 'FontCommand', () => { let editor, model, doc, root, command; beforeEach( () => { return ModelTestEditor.create() .then( newEditor => { editor = newEditor; model = editor.model; doc = model.document; root = doc.getRoot(); command = new FontCommand( editor, 'font' ); editor.commands.add( 'font', command ); model.schema.register( 'paragraph', { inheritAllFrom: '$block' } ); model.schema.register( 'img', { allowWhere: [ '$block', '$text' ], isObject: true } ); model.schema.extend( '$text', { allowAttributes: 'font' } ); } ); } ); afterEach( () => { editor.destroy(); } ); it( 'is a command', () => { expect( FontCommand.prototype ).to.be.instanceOf( Command ); expect( command ).to.be.instanceOf( Command ); } ); describe( 'value', () => { it( 'is set to font value when selection is in text with font attribute', () => { setData( model, '<$text font="foo">fo[]o' ); expect( command ).to.have.property( 'value', 'foo' ); } ); it( 'is undefined when selection is not in text with font attribute', () => { setData( model, 'fo[]o' ); expect( command ).to.have.property( 'value', undefined ); } ); } ); describe( 'isEnabled', () => { it( 'is true when selection is on text which can have font added', () => { setData( model, 'fo[]o' ); expect( command ).to.have.property( 'isEnabled', true ); } ); } ); describe( 'execute()', () => { it( 'should do nothing if the command is disabled', () => { setData( model, 'fo[ob]ar' ); command.isEnabled = false; command.execute( { value: 'foo' } ); expect( getData( model ) ).to.equal( 'fo[ob]ar' ); } ); it( 'should add font attribute on selected text', () => { setData( model, 'a[bc<$text font="foo">fo]obarxyz' ); expect( command.value ).to.be.undefined; command.execute( { value: 'foo' } ); expect( command.value ).to.equal( 'foo' ); expect( getData( model ) ).to.equal( 'a[<$text font="foo">bcfo]obarxyz' ); } ); it( 'should add font attribute on selected nodes (multiple nodes)', () => { setData( model, 'abcabc[abc' + 'foofoofoo' + 'barbar]bar' ); command.execute( { value: 'foo' } ); expect( command.value ).to.equal( 'foo' ); expect( getData( model ) ).to.equal( 'abcabc[<$text font="foo">abc' + '<$text font="foo">foofoofoo' + '<$text font="foo">barbar]bar' ); } ); it( 'should change font attribute on selected nodes', () => { setData( model, 'abc[abc<$text font="text-small">abc' + '<$text font="text-small">foofoofoo' + '<$text font="text-small">bar]barbar' ); command.execute( { value: 'foo' } ); expect( command.value ).to.equal( 'foo' ); expect( getData( model ) ).to.equal( 'abc[<$text font="foo">abcabc' + '<$text font="foo">foofoofoo' + '<$text font="foo">bar]<$text font="text-small">barbar' ); } ); it( 'should remove font attribute on selected nodes when passing undefined value', () => { setData( model, 'abcabc[<$text font="foo">abc' + '<$text font="foo">foofoofoo' + '<$text font="foo">barbar]bar' ); expect( command.value ).to.equal( 'foo' ); command.execute(); expect( command.value ).to.be.undefined; expect( getData( model ) ).to.equal( 'abcabc[abc' + 'foofoofoo' + 'barbar]bar' ); } ); it( 'should change selection attribute if selection is collapsed in non-empty parent', () => { setData( model, 'a[]bc<$text font="foo">foobarxyz' ); expect( command.value ).to.be.undefined; command.execute( { value: 'foo' } ); expect( command.value ).to.equal( 'foo' ); expect( doc.selection.hasAttribute( 'font' ) ).to.be.true; command.execute(); expect( command.value ).to.be.undefined; expect( doc.selection.hasAttribute( 'font' ) ).to.be.false; } ); it( 'should not store attribute change on selection if selection is collapsed in non-empty parent', () => { setData( model, 'a[]bc<$text font="foo">foobarxyz' ); command.execute( { value: 'foo' } ); // It should not save that bold was executed at position ( root, [ 0, 1 ] ). model.change( writer => { // Simulate clicking right arrow key by changing selection ranges. writer.setSelection( root.getNodeByPath( [ 0 ] ), 2 ); // Get back to previous selection. writer.setSelection( root.getNodeByPath( [ 0 ] ), 1 ); } ); expect( command.value ).to.be.undefined; } ); it( 'should change selection attribute and store it if selection is collapsed in empty parent', () => { setData( model, 'abc<$text font="foo">foobarxyz[]' ); expect( command.value ).to.be.undefined; command.execute( { value: 'foo' } ); expect( command.value ).to.equal( 'foo' ); expect( doc.selection.hasAttribute( 'font' ) ).to.be.true; // Attribute should be stored. // Simulate clicking somewhere else in the editor. model.change( writer => { writer.setSelection( root.getNodeByPath( [ 0 ] ), 2 ); } ); expect( command.value ).to.be.undefined; // Go back to where attribute was stored. model.change( writer => { writer.setSelection( root.getNodeByPath( [ 1 ] ), 0 ); } ); // Attribute should be restored. expect( command.value ).to.equal( 'foo' ); command.execute(); expect( command.value ).to.be.undefined; expect( doc.selection.hasAttribute( 'font' ) ).to.be.false; } ); it( 'should not apply attribute change where it would invalid schema', () => { model.schema.register( 'image', { inheritAllFrom: '$block' } ); setData( model, 'ab[c<$text font="foo">foobarxy]z' ); expect( command.isEnabled ).to.be.true; command.execute( { value: 'foo' } ); expect( getData( model ) ).to.equal( 'ab[<$text font="foo">c<$text font="foo">foobarxy]z' ); } ); it( 'should use parent batch for storing undo steps', () => { setData( model, 'a[bc<$text font="foo">fo]obarxyz' ); model.change( writer => { expect( writer.batch.operations.length ).to.equal( 0 ); command.execute( { value: 'foo' } ); expect( writer.batch.operations.length ).to.equal( 1 ); } ); expect( getData( model ) ).to.equal( 'a[<$text font="foo">bcfo]obarxyz' ); } ); describe( 'should cause firing model change event', () => { let spy; beforeEach( () => { spy = sinon.spy(); } ); it( 'collapsed selection in non-empty parent', () => { setData( model, 'x[]y' ); model.document.on( 'change', spy ); command.execute( { value: 'foo' } ); expect( spy.called ).to.be.true; } ); it( 'non-collapsed selection', () => { setData( model, '[xy]' ); model.document.on( 'change', spy ); command.execute( { value: 'foo' } ); expect( spy.called ).to.be.true; } ); it( 'in empty parent', () => { setData( model, '[]' ); model.document.on( 'change', spy ); command.execute( { value: 'foo' } ); expect( spy.called ).to.be.true; } ); } ); } ); } );