浏览代码

Integration with ParagraphCommand. Use getSelectedBlocks() in
HeadingCommand. Re-enabled command toggle. Renames in
config.heading.options.

Aleksander Nowodzinski 8 年之前
父节点
当前提交
d896a8b14e

+ 21 - 9
packages/ckeditor5-heading/src/heading.js

@@ -7,6 +7,8 @@
  * @module heading/heading
  */
 
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import HeadingCommand from './headingcommand';
 import HeadingEngine from './headingengine';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import Model from '@ckeditor/ckeditor5-ui/src/model';
@@ -24,7 +26,7 @@ export default class Heading extends Plugin {
 	 * @inheritDoc
 	 */
 	static get requires() {
-		return [ HeadingEngine ];
+		return [ Paragraph, HeadingEngine ];
 	}
 
 	/**
@@ -35,12 +37,22 @@ export default class Heading extends Plugin {
 		const headingEngine = editor.plugins.get( HeadingEngine );
 		const commands = headingEngine.commands;
 		const dropdownItems = new Collection();
+		let defaultCommand;
+
+		for ( let command of commands ) {
+			let modelElement, title;
+
+			if ( command instanceof HeadingCommand ) {
+				modelElement = command.modelElement;
+			} else {
+				modelElement = 'paragraph';
+				defaultCommand = command;
+			}
+
+			title = command.title;
 
-		for ( let { name, label } of commands ) {
 			// Add the option to the collection.
-			dropdownItems.add( new Model( {
-				name, label
-			} ) );
+			dropdownItems.add( new Model( { modelElement, label: title } ) );
 		}
 
 		// Create dropdown model.
@@ -59,12 +71,12 @@ export default class Heading extends Plugin {
 		dropdownModel.bind( 'label' ).to(
 			// Bind to #value of each command...
 			...getCommandsBindingTargets( commands, 'value' ),
-			// ...and chose the label of the first one which #value is true.
+			// ...and chose the title of the first one which #value is true.
 			( ...areActive ) => {
 				const index = areActive.findIndex( value => value );
 
 				// If none of the commands is active, display the first one.
-				return commands.get( index > -1 ? index : 0 ).label;
+				return index > -1 ? commands.get( index ).title : defaultCommand.title;
 			}
 		);
 
@@ -73,8 +85,8 @@ export default class Heading extends Plugin {
 			const dropdown = createListDropdown( dropdownModel, locale );
 
 			// Execute command when an item from the dropdown is selected.
-			this.listenTo( dropdown, 'execute', ( { source: { name } } ) => {
-				editor.execute( name );
+			this.listenTo( dropdown, 'execute', ( { source: { modelElement } } ) => {
+				editor.execute( modelElement );
 				editor.editing.view.focus();
 			} );
 

+ 36 - 81
packages/ckeditor5-heading/src/headingcommand.js

@@ -8,8 +8,6 @@
  */
 
 import Command from '@ckeditor/ckeditor5-core/src/command/command';
-import RootElement from '@ckeditor/ckeditor5-engine/src/model/rootelement';
-import camelCase from '@ckeditor/ckeditor5-utils/src/lib/lodash/camelCase';
 
 /**
  * The heading command. It is used by the {@link module:heading/heading~Heading heading feature} to apply headings.
@@ -29,18 +27,12 @@ export default class HeadingCommand extends Command {
 		Object.assign( this, option );
 
 		/**
-		 * Name of the command
+		 * Value of the command, indicating whether it is applied in the context
+		 * of current {@link module:engine/model/document~Document#selection selection}.
 		 *
 		 * @readonly
-		 * @member {String}
-		 */
-		this.name = camelCase( 'heading ' + this.id );
-
-		/**
-		 * TODO
-		 *
-		 * @readonly
-		 * @member {}
+		 * @observable
+		 * @member {Boolean}
 		 */
 		this.set( 'value', false );
 
@@ -49,26 +41,26 @@ export default class HeadingCommand extends Command {
 
 		/**
 		 * Unique identifier of the command, also element's name in the model.
-		 * See {@link module:heading/headingcommand~HeadingOption#id}.
+		 * See {@link module:heading/headingcommand~HeadingOption#modelElement}.
 		 *
 		 * @readonly
-		 * @member {String} #id
+		 * @member {String} #modelElement
 		 */
 
 		/**
 		 * Element this command creates in the view.
-		 * See {@link module:heading/headingcommand~HeadingOption#element}.
+		 * See {@link module:heading/headingcommand~HeadingOption#viewElement}.
 		 *
 		 * @readonly
-		 * @member {String} #element
+		 * @member {String} #viewElement
 		 */
 
 		/**
-		 * Label of this command.
-		 * See {@link module:heading/headingcommand~HeadingOption#label}.
+		 * User-readable title of the command.
+		 * See {@link module:heading/headingcommand~HeadingOption#title}.
 		 *
 		 * @readonly
-		 * @member {String} #label
+		 * @member {String} #title
 		 */
 	}
 
@@ -81,47 +73,36 @@ export default class HeadingCommand extends Command {
 	 * New batch will be created if this option is not set.
 	 */
 	_doExecute( options = {} ) {
-		const id = this.id;
-		const doc = this.editor.document;
-		const selection = doc.selection;
-		const startPosition = selection.getFirstPosition();
-		const elements = [];
-		// Storing selection ranges and direction to fix selection after renaming. See ckeditor5-engine#367.
+		const editor = this.editor;
+		const document = editor.document;
+		const selection = document.selection;
 		const ranges = [ ...selection.getRanges() ];
 		const isSelectionBackward = selection.isBackward;
 
-		// Collect elements to change option.
-		// This implementation may not be future proof but it's satisfactory at this stage.
-		if ( selection.isCollapsed ) {
-			const block = findTopmostBlock( startPosition );
+		// If current option is same as new option - toggle already applied option back to default one.
+		const shouldRemove = this.value;
 
-			if ( block ) {
-				elements.push( block );
-			}
-		} else {
-			for ( let range of ranges ) {
-				let startBlock = findTopmostBlock( range.start );
-				const endBlock = findTopmostBlock( range.end, false );
-
-				elements.push( startBlock );
+		document.enqueueChanges( () => {
+			const batch = options.batch || document.batch();
 
-				while ( startBlock !== endBlock ) {
-					startBlock = startBlock.nextSibling;
-					elements.push( startBlock );
+			for ( let element of document.selection.getSelectedBlocks() ) {
+				// When removing applied option.
+				if ( shouldRemove ) {
+					if ( element.name === this.modelElement ) {
+						// Apply paragraph to the single element only instead of working
+						// on the entire selection. Share the batch with the paragraph command.
+						editor.execute( 'paragraph', { element, batch } );
+					}
+				}
+				// When applying new option.
+				else {
+					batch.rename( element, this.modelElement );
 				}
-			}
-		}
-
-		doc.enqueueChanges( () => {
-			const batch = options.batch || doc.batch();
-
-			for ( let element of elements ) {
-				batch.rename( element, id );
 			}
 
 			// If range's selection start/end is placed directly in renamed block - we need to restore it's position
 			// after renaming, because renaming puts new element there.
-			doc.selection.setRanges( ranges, isSelectionBackward );
+			selection.setRanges( ranges, isSelectionBackward );
 		} );
 	}
 
@@ -131,45 +112,19 @@ export default class HeadingCommand extends Command {
 	 * @private
 	 */
 	_updateValue() {
-		const position = this.editor.document.selection.getFirstPosition();
-		const block = findTopmostBlock( position );
+		const block = this.editor.document.selection.getSelectedBlocks().next().value;
 
 		if ( block ) {
-			this.value = this.id == block.name;
+			this.value = this.modelElement == block.name;
 		}
 	}
 }
 
-// Looks for the topmost element in the position's ancestor (up to an element in the root).
-//
-// NOTE: This method does not check the schema directly — it assumes that only block elements can be placed directly inside
-// the root.
-//
-// @private
-// @param {engine.model.Position} position
-// @param {Boolean} [nodeAfter=true] When the position is placed inside the root element, this will determine if the element before
-// or after a given position will be returned.
-// @returns {engine.model.Element}
-function findTopmostBlock( position, nodeAfter = true ) {
-	let parent = position.parent;
-
-	// If position is placed inside root - get element after/before it.
-	if ( parent instanceof RootElement ) {
-		return nodeAfter ? position.nodeAfter : position.nodeBefore;
-	}
-
-	while ( !( parent.parent instanceof RootElement ) ) {
-		parent = parent.parent;
-	}
-
-	return parent;
-}
-
 /**
  * Heading option descriptor.
  *
  * @typedef {Object} module:heading/headingcommand~HeadingOption
- * @property {String} id Option identifier. It will be used as the element's name in the model.
- * @property {String} element The name of the view element that will be used to represent the model element in the view.
- * @property {String} label The display name of the option.
+ * @property {String} modelElement Element's name in the model.
+ * @property {String} viewElement The name of the view element that will be used to represent the model element in the view.
+ * @property {String} title The user-readable title of the option.
  */

+ 25 - 21
packages/ckeditor5-heading/src/headingengine.js

@@ -14,7 +14,7 @@ import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildv
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import HeadingCommand from './headingcommand';
 
-const defaultOptionId = 'paragraph';
+const defaultModelElement = 'paragraph';
 
 /**
  * The headings engine feature. It handles switching between block formats – headings and paragraph.
@@ -35,14 +35,14 @@ export default class HeadingEngine extends Plugin {
 		 * @readonly
 		 * @member {module:utils/collection~Collection.<module:heading/headingcommand~HeadingCommand>}
 		 */
-		this.commands = new Collection();
+		this.commands = new Collection( { idProperty: 'modelElement' } );
 
 		editor.config.define( 'heading', {
 			options: [
-				{ id: 'paragraph', element: 'p', label: 'Paragraph' },
-				{ id: 'heading1', element: 'h2', label: 'Heading 1' },
-				{ id: 'heading2', element: 'h3', label: 'Heading 2' },
-				{ id: 'heading3', element: 'h4', label: 'Heading 3' }
+				{ modelElement: 'paragraph' },
+				{ modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' },
+				{ modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2' },
+				{ modelElement: 'heading3', viewElement: 'h4', title: 'Heading 3' }
 			]
 		} );
 	}
@@ -62,28 +62,32 @@ export default class HeadingEngine extends Plugin {
 		const data = editor.data;
 		const editing = editor.editing;
 		const options = this._getLocalizedOptions();
+		let command;
 
 		for ( let option of options ) {
 			// Skip paragraph - it is defined in required Paragraph feature.
-			if ( option.id !== defaultOptionId ) {
+			if ( option.modelElement !== defaultModelElement ) {
 				// Schema.
-				editor.document.schema.registerItem( option.id, '$block' );
+				editor.document.schema.registerItem( option.modelElement, '$block' );
 
 				// Build converter from model to view for data and editing pipelines.
 				buildModelConverter().for( data.modelToView, editing.modelToView )
-					.fromElement( option.id )
-					.toElement( option.element );
+					.fromElement( option.modelElement )
+					.toElement( option.viewElement );
 
 				// Build converter from view to model for data pipeline.
 				buildViewConverter().for( data.viewToModel )
-					.fromElement( option.element )
-					.toElement( option.id );
+					.fromElement( option.viewElement )
+					.toElement( option.modelElement );
+
+				// Register the heading command for this option.
+				command = new HeadingCommand( editor, option );
+				editor.commands.set( command.modelElement, command );
+			} else {
+				command = editor.commands.get( defaultModelElement );
 			}
 
-			// Register the heading command for this option.
-			const command = new HeadingCommand( editor, option );
 			this.commands.add( command );
-			editor.commands.set( command.name, command );
 		}
 	}
 
@@ -101,10 +105,10 @@ export default class HeadingEngine extends Plugin {
 			this.listenTo( enterCommand, 'afterExecute', ( evt, data ) => {
 				const positionParent = editor.document.selection.getFirstPosition().parent;
 				const batch = data.batch;
-				const isHeading = options.some( option => option.id == positionParent.name );
+				const isHeading = options.some( option => option.modelElement == positionParent.name );
 
-				if ( isHeading && positionParent.name != defaultOptionId && positionParent.childCount === 0 ) {
-					batch.rename( positionParent, defaultOptionId );
+				if ( isHeading && positionParent.name != defaultModelElement && positionParent.childCount === 0 ) {
+					batch.rename( positionParent, defaultModelElement );
 				}
 			} );
 		}
@@ -112,7 +116,7 @@ export default class HeadingEngine extends Plugin {
 
 	/**
 	 * Returns heading options as defined in `config.heading.options` but processed to consider
-	 * editor localization, i.e. to display {@link module:heading/headingcommand~HeadingOption#label}
+	 * editor localization, i.e. to display {@link module:heading/headingcommand~HeadingOption#title}
 	 * in the correct language.
 	 *
 	 * Note: The reason behind this method is that there's no way to use {@link utils/locale~Locale#t}
@@ -145,10 +149,10 @@ export default class HeadingEngine extends Plugin {
 		 */
 		this._cachedLocalizedOptions = editor.config.get( 'heading.options' )
 			.map( option => {
-				if ( localizedLabels[ option.label ] ) {
+				if ( localizedLabels[ option.title ] ) {
 					// Clone the option to avoid altering the original `config.heading.options`.
 					option = Object.assign( {}, option, {
-						label: localizedLabels[ option.label ]
+						title: localizedLabels[ option.title ]
 					} );
 				}
 

+ 9 - 9
packages/ckeditor5-heading/tests/heading.js

@@ -63,18 +63,18 @@ describe( 'Heading', () => {
 			const executeSpy = testUtils.sinon.spy( editor, 'execute' );
 			const dropdown = editor.ui.componentFactory.create( 'headings' );
 
-			dropdown.name = 'headingParagraph';
+			dropdown.modelElement = 'paragraph';
 			dropdown.fire( 'execute' );
 
 			sinon.assert.calledOnce( executeSpy );
-			sinon.assert.calledWithExactly( executeSpy, 'headingParagraph' );
+			sinon.assert.calledWithExactly( executeSpy, 'paragraph' );
 		} );
 
 		it( 'should focus view after command execution', () => {
 			const focusSpy = testUtils.sinon.spy( editor.editing.view, 'focus' );
 			const dropdown = editor.ui.componentFactory.create( 'headings' );
 
-			dropdown.name = 'headingParagraph';
+			dropdown.modelElement = 'paragraph';
 			dropdown.fire( 'execute' );
 
 			sinon.assert.calledOnce( focusSpy );
@@ -122,9 +122,9 @@ describe( 'Heading', () => {
 					lang: 'pl',
 					heading: {
 						options: [
-							{ id: 'paragraph', element: 'p', label: 'Paragraph' },
-							{ id: 'heading1', element: 'h2', label: 'Heading 1' },
-							{ id: 'heading2', element: 'h3', label: 'Not automatically localized' }
+							{ modelElement: 'paragraph', viewElement: 'p', title: 'Paragraph' },
+							{ modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' },
+							{ modelElement: 'heading2', viewElement: 'h3', title: 'Not automatically localized' }
 						]
 					}
 				} )
@@ -137,9 +137,9 @@ describe( 'Heading', () => {
 
 			it( 'does not alter the original config', () => {
 				expect( editor.config.get( 'heading.options' ) ).to.deep.equal( [
-					{ id: 'paragraph', element: 'p', label: 'Paragraph' },
-					{ id: 'heading1', element: 'h2', label: 'Heading 1' },
-					{ id: 'heading2', element: 'h3', label: 'Not automatically localized' }
+					{ modelElement: 'paragraph', viewElement: 'p', title: 'Paragraph' },
+					{ modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' },
+					{ modelElement: 'heading2', viewElement: 'h3', title: 'Not automatically localized' }
 				] );
 			} );
 

+ 64 - 49
packages/ckeditor5-heading/tests/headingcommand.js

@@ -4,15 +4,15 @@
  */
 
 import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
+import ParagraphCommand from '@ckeditor/ckeditor5-paragraph/src/paragraphcommand';
 import HeadingCommand from '../src/headingcommand';
 import Range from '@ckeditor/ckeditor5-engine/src/model/range';
 import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 
 const options = [
-	{ id: 'paragraph', element: 'p', label: 'P' },
-	{ id: 'heading1', element: 'h2', label: 'H2' },
-	{ id: 'heading2', element: 'h3', label: 'H3' },
-	{ id: 'heading3', element: 'h4', label: 'H4' }
+	{ modelElement: 'heading1', viewElement: 'h2', title: 'H2' },
+	{ modelElement: 'heading2', viewElement: 'h3', title: 'H3' },
+	{ modelElement: 'heading3', viewElement: 'h4', title: 'H4' }
 ];
 
 describe( 'HeadingCommand', () => {
@@ -25,9 +25,12 @@ describe( 'HeadingCommand', () => {
 			commands = {};
 			schema = document.schema;
 
+			editor.commands.set( 'paragraph', new ParagraphCommand( editor ) );
+			schema.registerItem( 'paragraph', '$block' );
+
 			for ( let option of options ) {
-				commands[ option.id ] = new HeadingCommand( editor, option );
-				schema.registerItem( option.id, '$block' );
+				commands[ option.modelElement ] = new HeadingCommand( editor, option );
+				schema.registerItem( option.modelElement, '$block' );
 			}
 
 			root = document.getRoot();
@@ -35,8 +38,8 @@ describe( 'HeadingCommand', () => {
 	} );
 
 	afterEach( () => {
-		for ( let id in commands ) {
-			commands[ id ].destroy();
+		for ( let modelElement in commands ) {
+			commands[ modelElement ].destroy();
 		}
 	} );
 
@@ -45,11 +48,11 @@ describe( 'HeadingCommand', () => {
 			test( option );
 		}
 
-		function test( { id, element, label } ) {
-			it( `are set for option.id = ${ id }`, () => {
-				expect( commands[ id ].id ).to.equal( id );
-				expect( commands[ id ].element ).to.equal( element );
-				expect( commands[ id ].label ).to.equal( label );
+		function test( { modelElement, viewElement, title } ) {
+			it( `are set for option.modelElement = ${ modelElement }`, () => {
+				expect( commands[ modelElement ].modelElement ).to.equal( modelElement );
+				expect( commands[ modelElement ].viewElement ).to.equal( viewElement );
+				expect( commands[ modelElement ].title ).to.equal( title );
 			} );
 		}
 	} );
@@ -59,13 +62,13 @@ describe( 'HeadingCommand', () => {
 			test( option );
 		}
 
-		function test( { id } ) {
-			it( `equals ${ id } when collapsed selection is placed inside ${ id } element`, () => {
-				setData( document, `<${ id }>foobar</${ id }>` );
+		function test( { modelElement } ) {
+			it( `equals ${ modelElement } when collapsed selection is placed inside ${ modelElement } element`, () => {
+				setData( document, `<${ modelElement }>foobar</${ modelElement }>` );
 				const element = root.getChild( 0 );
 				document.selection.addRange( Range.createFromParentsAndOffsets( element, 3, element, 3 ) );
 
-				expect( commands[ id ].value ).to.be.true;
+				expect( commands[ modelElement ].value ).to.be.true;
 			} );
 		}
 	} );
@@ -94,6 +97,19 @@ describe( 'HeadingCommand', () => {
 
 				expect( batch.deltas.length ).to.be.above( 0 );
 			} );
+
+			it( 'should use provided batch (converting to default option)', () => {
+				const batch = editor.document.batch();
+				const command = commands.heading1;
+
+				setData( document, '<heading1>foo[]bar</heading1>' );
+
+				expect( batch.deltas.length ).to.equal( 0 );
+
+				command._doExecute( { batch } );
+
+				expect( batch.deltas.length ).to.be.above( 0 );
+			} );
 		} );
 
 		describe( 'collapsed selection', () => {
@@ -104,38 +120,31 @@ describe( 'HeadingCommand', () => {
 				convertTo = option;
 			}
 
-			it( 'uses paragraph as default value', () => {
+			it( 'converts to default option when executed with already applied option', () => {
+				const command = commands.heading1;
+
 				setData( document, '<heading1>foo[]bar</heading1>' );
-				commands.paragraph._doExecute();
+				command._doExecute( { modelElement: 'heading1' } );
 
 				expect( getData( document ) ).to.equal( '<paragraph>foo[]bar</paragraph>' );
 			} );
 
-			// it( 'converts to default option when executed with already applied option', () => {
-			// 	const command = commands.paragraph;
-
-			// 	setData( document, '<heading1>foo[]bar</heading1>' );
-			// 	command._doExecute( { id: 'heading1' } );
-
-			// 	expect( getData( document ) ).to.equal( '<paragraph>foo[]bar</paragraph>' );
-			// } );
-
 			it( 'converts topmost blocks', () => {
 				schema.registerItem( 'inlineImage', '$inline' );
 				schema.allow( { name: '$text', inside: 'inlineImage' } );
 
-				setData( document, '<heading1><inlineImage>foo[]</inlineImage>bar</heading1>' );
-				commands.paragraph._doExecute();
+				setData( document, '<paragraph><inlineImage>foo[]</inlineImage>bar</paragraph>' );
+				commands.heading1._doExecute();
 
-				expect( getData( document ) ).to.equal( '<paragraph><inlineImage>foo[]</inlineImage>bar</paragraph>' );
+				expect( getData( document ) ).to.equal( '<heading1><inlineImage>foo[]</inlineImage>bar</heading1>' );
 			} );
 
 			function test( from, to ) {
-				it( `converts ${ from.id } to ${ to.id } on collapsed selection`, () => {
-					setData( document, `<${ from.id }>foo[]bar</${ from.id }>` );
-					commands[ to.id ]._doExecute();
+				it( `converts ${ from.modelElement } to ${ to.modelElement } on collapsed selection`, () => {
+					setData( document, `<${ from.modelElement }>foo[]bar</${ from.modelElement }>` );
+					commands[ to.modelElement ]._doExecute();
 
-					expect( getData( document ) ).to.equal( `<${ to.id }>foo[]bar</${ to.id }>` );
+					expect( getData( document ) ).to.equal( `<${ to.modelElement }>foo[]bar</${ to.modelElement }>` );
 				} );
 			}
 		} );
@@ -150,28 +159,34 @@ describe( 'HeadingCommand', () => {
 
 			it( 'converts all elements where selection is applied', () => {
 				setData( document, '<heading1>foo[</heading1><heading2>bar</heading2><heading2>]baz</heading2>' );
-				commands.paragraph._doExecute();
+				commands.heading3._doExecute();
 
 				expect( getData( document ) ).to.equal(
-					'<paragraph>foo[</paragraph><paragraph>bar</paragraph><paragraph>]baz</paragraph>'
+					'<heading3>foo[</heading3><heading3>bar</heading3><heading3>]baz</heading3>'
 				);
 			} );
 
-			// it( 'resets to default value all elements with same option', () => {
-			// 	setData( document, '<heading1>foo[</heading1><heading1>bar</heading1><heading2>baz</heading2>]' );
-			// 	commands.heading1._doExecute();
+			it( 'resets to default value all elements with same option', () => {
+				setData( document, '<heading1>foo[</heading1><heading1>bar</heading1><heading2>baz</heading2>]' );
+				commands.heading1._doExecute();
 
-			// 	expect( getData( document ) ).to.equal(
-			// 		'<paragraph>foo[</paragraph><paragraph>bar</paragraph><heading2>baz</heading2>]'
-			// 	);
-			// } );
+				expect( getData( document ) ).to.equal(
+					'<paragraph>foo[</paragraph><paragraph>bar</paragraph><heading2>baz</heading2>]'
+				);
+			} );
 
-			function test( from, to ) {
-				it( `converts ${ from.id } to ${ to.id } on non-collapsed selection`, () => {
-					setData( document, `<${ from.id }>foo[bar</${ from.id }><${ from.id }>baz]qux</${ from.id }>` );
-					commands[ to.id ]._doExecute();
+			function test( { modelElement: fromElement }, { modelElement: toElement } ) {
+				it( `converts ${ fromElement } to ${ toElement } on non-collapsed selection`, () => {
+					setData(
+						document,
+						`<${ fromElement }>foo[bar</${ fromElement }><${ fromElement }>baz]qux</${ fromElement }>`
+					);
+
+					commands[ toElement ]._doExecute();
 
-					expect( getData( document ) ).to.equal( `<${ to.id }>foo[bar</${ to.id }><${ to.id }>baz]qux</${ to.id }>` );
+					expect( getData( document ) ).to.equal(
+						`<${ toElement }>foo[bar</${ toElement }><${ toElement }>baz]qux</${ toElement }>`
+					);
 				} );
 			}
 		} );

+ 12 - 11
packages/ckeditor5-heading/tests/headingengine.js

@@ -4,9 +4,10 @@
  */
 
 import HeadingEngine from '../src/headingengine';
+import HeadingCommand from '../src/headingcommand';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import ParagraphCommand from '@ckeditor/ckeditor5-paragraph/src/paragraphcommand';
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
-import HeadingCommand from '../src/headingcommand';
 import Enter from '@ckeditor/ckeditor5-enter/src/enter';
 import { add } from '@ckeditor/ckeditor5-utils/src/translation-service';
 import { getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
@@ -55,10 +56,10 @@ describe( 'HeadingEngine', () => {
 	} );
 
 	it( 'should register #commands', () => {
-		expect( editor.commands.get( 'headingParagraph' ) ).to.be.instanceOf( HeadingCommand );
-		expect( editor.commands.get( 'headingHeading1' ) ).to.be.instanceOf( HeadingCommand );
-		expect( editor.commands.get( 'headingHeading2' ) ).to.be.instanceOf( HeadingCommand );
-		expect( editor.commands.get( 'headingHeading3' ) ).to.be.instanceOf( HeadingCommand );
+		expect( editor.commands.get( 'paragraph' ) ).to.be.instanceOf( ParagraphCommand );
+		expect( editor.commands.get( 'heading1' ) ).to.be.instanceOf( HeadingCommand );
+		expect( editor.commands.get( 'heading2' ) ).to.be.instanceOf( HeadingCommand );
+		expect( editor.commands.get( 'heading3' ) ).to.be.instanceOf( HeadingCommand );
 	} );
 
 	it( 'should convert heading1', () => {
@@ -112,18 +113,18 @@ describe( 'HeadingEngine', () => {
 			describe( 'default value', () => {
 				it( 'should be set', () => {
 					expect( editor.config.get( 'heading.options' ) ).to.deep.equal( [
-						{ id: 'paragraph', element: 'p', label: 'Paragraph' },
-						{ id: 'heading1', element: 'h2', label: 'Heading 1' },
-						{ id: 'heading2', element: 'h3', label: 'Heading 2' },
-						{ id: 'heading3', element: 'h4', label: 'Heading 3' }
+						{ modelElement: 'paragraph' },
+						{ modelElement: 'heading1', viewElement: 'h2', title: 'Heading 1' },
+						{ modelElement: 'heading2', viewElement: 'h3', title: 'Heading 2' },
+						{ modelElement: 'heading3', viewElement: 'h4', title: 'Heading 3' }
 					] );
 				} );
 			} );
 
 			it( 'should customize options', () => {
 				const options = [
-					{ id: 'paragraph', element: 'p', label: 'Paragraph' },
-					{ id: 'h4', element: 'h4', label: 'H4' }
+					{ modelElement: 'paragraph', viewElement: 'p', title: 'Paragraph' },
+					{ modelElement: 'h4', viewElement: 'h4', title: 'H4' }
 				];
 
 				return VirtualTestEditor.create( {