8
0
Просмотр исходного кода

Other: Removed `ToggleAttributeCommand` class as well as other helpers from the `core/command` namespace. Closes #14.

BREAKING CHANGES: The `ToggleAttributeCommand` was moved to the `ckeditor5-basic-styles` package as `AttributeCommand` and the other command helpers to `ckeditor5-engine` as `Schema` methods.
Piotrek Koszuliński 8 лет назад
Родитель
Сommit
b4d49cfab5

+ 0 - 56
packages/ckeditor5-core/src/command/helpers/getschemavalidranges.js

@@ -1,56 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module core/command/helpers/getschemavalidranges
- */
-
-import TreeWalker from '@ckeditor/ckeditor5-engine/src/model/treewalker';
-import Range from '@ckeditor/ckeditor5-engine/src/model/range';
-import Position from '@ckeditor/ckeditor5-engine/src/model/position';
-
-/**
- * Walks through given array of ranges and removes parts of them that are not allowed by passed schema to have the
- * attribute set. This is done by breaking a range in two and omitting the not allowed part.
- *
- * @param {String} attribute Attribute key.
- * @param {Array.<module:engine/model/range~Range>} ranges Ranges to be validated.
- * @param {module:engine/model/schema~Schema} schema Document schema.
- * @returns {Array.<module:engine/model/range~Range>} Ranges without invalid parts.
- */
-export default function getSchemaValidRanges( attribute, ranges, schema ) {
-	const validRanges = [];
-
-	for ( const range of ranges ) {
-		const walker = new TreeWalker( { boundaries: range, mergeCharacters: true } );
-		let step = walker.next();
-
-		let last = range.start;
-		let from = range.start;
-		const to = range.end;
-
-		while ( !step.done ) {
-			const name = step.value.item.name || '$text';
-			const itemPosition = Position.createBefore( step.value.item );
-
-			if ( !schema.check( { name, inside: itemPosition, attributes: attribute } ) ) {
-				if ( !from.isEqual( last ) ) {
-					validRanges.push( new Range( from, last ) );
-				}
-
-				from = walker.position;
-			}
-
-			last = walker.position;
-			step = walker.next();
-		}
-
-		if ( from && !from.isEqual( to ) ) {
-			validRanges.push( new Range( from, to ) );
-		}
-	}
-
-	return validRanges;
-}

+ 0 - 54
packages/ckeditor5-core/src/command/helpers/isattributeallowedinselection.js

@@ -1,54 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module core/command/helpers/isattributeallowedinselection
- */
-
-import TreeWalker from '@ckeditor/ckeditor5-engine/src/model/treewalker';
-
-/**
- * Checks {@link module:engine/model/document~Document#schema} if attribute is allowed in selection:
- *
- * * if selection is on range, the command is enabled if any of nodes in that range can have bold,
- * * if selection is collapsed, the command is enabled if text with bold is allowed in that node.
- *
- * @param {String} attribute Attribute key.
- * @param {module:engine/model/selection~Selection} selection Selection which ranges will be validate.
- * @param {module:engine/model/schema~Schema} schema Document schema.
- * @returns {Boolean}
- */
-export default function isAttributeAllowedInSelection( attribute, selection, schema ) {
-	if ( selection.isCollapsed ) {
-		// Check whether schema allows for a test with `attributeKey` in caret position.
-		return schema.check( { name: '$text', inside: selection.getFirstPosition(), attributes: attribute } );
-	} else {
-		const ranges = selection.getRanges();
-
-		// For all ranges, check nodes in them until you find a node that is allowed to have `attributeKey` attribute.
-		for ( const range of ranges ) {
-			const walker = new TreeWalker( { boundaries: range, mergeCharacters: true } );
-			let last = walker.position;
-			let step = walker.next();
-
-			// Walk the range.
-			while ( !step.done ) {
-				// If returned item does not have name property, it is a model.TextFragment.
-				const name = step.value.item.name || '$text';
-
-				if ( schema.check( { name, inside: last, attributes: attribute } ) ) {
-					// If we found a node that is allowed to have the attribute, return true.
-					return true;
-				}
-
-				last = walker.position;
-				step = walker.next();
-			}
-		}
-	}
-
-	// If we haven't found such node, return false.
-	return false;
-}

+ 0 - 109
packages/ckeditor5-core/src/command/toggleattributecommand.js

@@ -1,109 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module core/command/toggleattributecommand
- */
-
-import Command from '../command';
-import getSchemaValidRanges from './helpers/getschemavalidranges';
-import isAttributeAllowedInSelection from './helpers/isattributeallowedinselection';
-
-/**
- * An extension of the base {@link module:core/command~Command} class, which provides utilities for a command which toggles a single
- * attribute on a text or an element. `ToggleAttributeCommand` uses {@link module:engine/model/document~Document#selection}
- * to decide which nodes (if any) should be changed, and applies or removes attributes from them.
- *
- * The command checks {@link module:engine/model/document~Document#schema} to decide if it should be enabled.
- *
- * @extends module:core/command~Command
- */
-export default class ToggleAttributeCommand extends Command {
-	/**
-	 * @param {module:core/editor/editor~Editor} editor
-	 * @param {String} attributeKey Attribute that will be set by the command.
-	 */
-	constructor( editor, attributeKey ) {
-		super( editor );
-
-		/**
-		 * Attribute that will be set by the command.
-		 *
-		 * @readonly
-		 * @member {String}
-		 */
-		this.attributeKey = attributeKey;
-
-		/**
-		 * Flag indicating whether the command is active. For collapsed selection it means that typed characters will have
-		 * the command's attribute set. For range selection it means that all nodes inside have the attribute applied.
-		 *
-		 * @observable
-		 * @readonly
-		 * @member {Boolean} #value
-		 */
-	}
-
-	/**
-	 * Updates command's {@link #value} based on the current selection.
-	 */
-	refresh() {
-		const doc = this.editor.document;
-
-		this.value = doc.selection.hasAttribute( this.attributeKey );
-		this.isEnabled = isAttributeAllowedInSelection( this.attributeKey, doc.selection, doc.schema );
-	}
-
-	/**
-	 * Executes the command: adds or removes attributes to nodes or selection.
-	 *
-	 * If the command is active (`value == true`), it will remove attributes. Otherwise, it will set attributes.
-	 *
-	 * The execution result differs, depending on the {@link module:engine/model/document~Document#selection}:
-	 * * if selection is on a range, the command applies the attribute on all nodes in that ranges
-	 * (if they are allowed to have this attribute by the {@link module:engine/model/schema~Schema schema}),
-	 * * if selection is collapsed in non-empty node, the command applies attribute to the
-	 * {@link module:engine/model/document~Document#selection} itself (note that typed characters copy attributes from selection),
-	 * * if selection is collapsed in empty node, the command applies attribute to the parent node of selection (note
-	 * that selection inherits all attributes from a node if it is in empty node).
-	 *
-	 * If the command is disabled (`isEnabled == false`) when it is executed, nothing will happen.
-	 *
-	 * @fires execute
-	 * @param {Object} [options] Options of command.
-	 * @param {Boolean} [options.forceValue] If set it will force command behavior. If `true`, command will apply attribute,
-	 * otherwise command will remove attribute. If not set, command will look for it's current value to decide what it should do.
-	 * @param {module:engine/model/batch~Batch} [options.batch] Batch to group undo steps.
-	 */
-	execute( options = {} ) {
-		const doc = this.editor.document;
-		const selection = doc.selection;
-		const value = ( options.forceValue === undefined ) ? !this.value : options.forceValue;
-
-		// If selection has non-collapsed ranges, we change attribute on nodes inside those ranges.
-		doc.enqueueChanges( () => {
-			if ( selection.isCollapsed ) {
-				if ( value ) {
-					selection.setAttribute( this.attributeKey, true );
-				} else {
-					selection.removeAttribute( this.attributeKey );
-				}
-			} else {
-				const ranges = getSchemaValidRanges( this.attributeKey, selection.getRanges(), doc.schema );
-
-				// Keep it as one undo step.
-				const batch = options.batch || doc.batch();
-
-				for ( const range of ranges ) {
-					if ( value ) {
-						batch.setAttribute( range, this.attributeKey, value );
-					} else {
-						batch.removeAttribute( range, this.attributeKey );
-					}
-				}
-			}
-		} );
-	}
-}

+ 0 - 83
packages/ckeditor5-core/tests/command/helpers/getschemavalidranges.js

@@ -1,83 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-import Document from '@ckeditor/ckeditor5-engine/src/model/document';
-import Range from '@ckeditor/ckeditor5-engine/src/model/range';
-import Selection from '@ckeditor/ckeditor5-engine/src/model/selection';
-import getSchemaValidRanges from '../../../src/command/helpers/getschemavalidranges';
-import { setData, stringify } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
-
-describe( 'getSchemaValidRanges', () => {
-	const attribute = 'bold';
-	let document, root, schema, ranges;
-
-	beforeEach( () => {
-		document = new Document();
-		schema = document.schema;
-		root = document.createRoot();
-
-		schema.registerItem( 'p', '$block' );
-		schema.registerItem( 'h1', '$block' );
-		schema.registerItem( 'img', '$inline' );
-
-		schema.allow( { name: '$text', attributes: 'bold', inside: 'p' } );
-		schema.allow( { name: 'p', attributes: 'bold', inside: '$root' } );
-
-		setData( document, '<p>foo<img />bar</p>' );
-		ranges = [ Range.createOn( root.getChild( 0 ) ) ];
-	} );
-
-	it( 'should return unmodified ranges when attribute is allowed on each item (v1 – text is not allowed in img)', () => {
-		schema.allow( { name: 'img', attributes: 'bold', inside: 'p' } );
-
-		expect( getSchemaValidRanges( attribute, ranges, schema ) ).to.deep.equal( ranges );
-	} );
-
-	it( 'should return unmodified ranges when attribute is allowed on each item (v2 – text is allowed in img)', () => {
-		schema.allow( { name: 'img', attributes: 'bold', inside: 'p' } );
-		schema.allow( { name: '$text', inside: 'img' } );
-
-		expect( getSchemaValidRanges( attribute, ranges, schema ) ).to.deep.equal( ranges );
-	} );
-
-	it( 'should return two ranges when attribute is not allowed on one item', () => {
-		schema.allow( { name: 'img', attributes: 'bold', inside: 'p' } );
-		schema.allow( { name: '$text', inside: 'img' } );
-
-		setData( document, '<p>foo<img>xxx</img>bar</p>' );
-
-		const validRanges = getSchemaValidRanges( attribute, ranges, schema );
-		const sel = new Selection();
-		sel.setRanges( validRanges );
-
-		expect( stringify( root, sel ) ).to.equal( '[<p>foo<img>]xxx[</img>bar</p>]' );
-	} );
-
-	it( 'should return three ranges when attribute is not allowed on one element but is allowed on its child', () => {
-		schema.allow( { name: '$text', inside: 'img' } );
-		schema.allow( { name: '$text', attributes: 'bold', inside: 'img' } );
-
-		setData( document, '<p>foo<img>xxx</img>bar</p>' );
-
-		const validRanges = getSchemaValidRanges( attribute, ranges, schema );
-		const sel = new Selection();
-		sel.setRanges( validRanges );
-
-		expect( stringify( root, sel ) ).to.equal( '[<p>foo]<img>[xxx]</img>[bar</p>]' );
-	} );
-
-	it( 'should split range into two ranges and omit disallowed element', () => {
-		// Disallow bold on img.
-		document.schema.disallow( { name: 'img', attributes: 'bold', inside: 'p' } );
-
-		const result = getSchemaValidRanges( attribute, ranges, schema );
-
-		expect( result ).to.length( 2 );
-		expect( result[ 0 ].start.path ).to.members( [ 0 ] );
-		expect( result[ 0 ].end.path ).to.members( [ 0, 3 ] );
-		expect( result[ 1 ].start.path ).to.members( [ 0, 4 ] );
-		expect( result[ 1 ].end.path ).to.members( [ 1 ] );
-	} );
-} );

+ 0 - 74
packages/ckeditor5-core/tests/command/helpers/isattributeallowedinselection.js

@@ -1,74 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-import Document from '@ckeditor/ckeditor5-engine/src/model/document';
-import isAttributeAllowedInSelection from '../../../src/command/helpers/isattributeallowedinselection';
-import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
-
-describe( 'isAttributeAllowedInSelection', () => {
-	const attribute = 'bold';
-	let document;
-
-	beforeEach( () => {
-		document = new Document();
-		document.createRoot();
-
-		document.schema.registerItem( 'p', '$block' );
-		document.schema.registerItem( 'h1', '$block' );
-		document.schema.registerItem( 'img', '$inline' );
-
-		// Bold text is allowed only in P.
-		document.schema.allow( { name: '$text', attributes: 'bold', inside: 'p' } );
-		document.schema.allow( { name: 'p', attributes: 'bold', inside: '$root' } );
-
-		// Disallow bold on image.
-		document.schema.disallow( { name: 'img', attributes: 'bold', inside: '$root' } );
-	} );
-
-	describe( 'when selection is collapsed', () => {
-		it( 'should return true if characters with the attribute can be placed at caret position', () => {
-			setData( document, '<p>f[]oo</p>' );
-			expect( isAttributeAllowedInSelection( attribute, document.selection, document.schema ) ).to.be.true;
-		} );
-
-		it( 'should return false if characters with the attribute cannot be placed at caret position', () => {
-			setData( document, '<h1>[]</h1>' );
-			expect( isAttributeAllowedInSelection( attribute, document.selection, document.schema ) ).to.be.false;
-
-			setData( document, '[]' );
-			expect( isAttributeAllowedInSelection( attribute, document.selection, document.schema ) ).to.be.false;
-		} );
-	} );
-
-	describe( 'when selection is not collapsed', () => {
-		it( 'should return true if there is at least one node in selection that can have the attribute', () => {
-			// Simple selection on a few characters.
-			setData( document, '<p>[foo]</p>' );
-			expect( isAttributeAllowedInSelection( attribute, document.selection, document.schema ) ).to.be.true;
-
-			// Selection spans over characters but also include nodes that can't have attribute.
-			setData( document, '<p>fo[o<img />b]ar</p>' );
-			expect( isAttributeAllowedInSelection( attribute, document.selection, document.schema ) ).to.be.true;
-
-			// Selection on whole root content. Characters in P can have an attribute so it's valid.
-			setData( document, '[<p>foo<img />bar</p><h1></h1>]' );
-			expect( isAttributeAllowedInSelection( attribute, document.selection, document.schema ) ).to.be.true;
-
-			// Selection on empty P. P can have the attribute.
-			setData( document, '[<p></p>]' );
-			expect( isAttributeAllowedInSelection( attribute, document.selection, document.schema ) ).to.be.true;
-		} );
-
-		it( 'should return false if there are no nodes in selection that can have the attribute', () => {
-			// Selection on DIV which can't have bold text.
-			setData( document, '[<h1></h1>]' );
-			expect( isAttributeAllowedInSelection( attribute, document.selection, document.schema ) ).to.be.false;
-
-			// Selection on two images which can't be bold.
-			setData( document, '<p>foo[<img /><img />]bar</p>' );
-			expect( isAttributeAllowedInSelection( attribute, document.selection, document.schema ) ).to.be.false;
-		} );
-	} );
-} );

+ 0 - 279
packages/ckeditor5-core/tests/command/toggleattributecommand.js

@@ -1,279 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-import Editor from '../../src/editor/editor';
-import Document from '@ckeditor/ckeditor5-engine/src/model/document';
-import Batch from '@ckeditor/ckeditor5-engine/src/model/batch';
-import ToggleAttributeCommand from '../../src/command/toggleattributecommand';
-import Range from '@ckeditor/ckeditor5-engine/src/model/range';
-import Position from '@ckeditor/ckeditor5-engine/src/model/position';
-import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
-
-describe( 'ToggleAttributeCommand', () => {
-	const attrKey = 'bold';
-	let editor, command, modelDoc, root;
-
-	beforeEach( () => {
-		editor = new Editor();
-		editor.document = new Document();
-
-		modelDoc = editor.document;
-		root = modelDoc.createRoot();
-
-		command = new ToggleAttributeCommand( editor, attrKey );
-
-		modelDoc.schema.registerItem( 'p', '$block' );
-		modelDoc.schema.registerItem( 'h1', '$block' );
-		modelDoc.schema.registerItem( 'img', '$inline' );
-
-		// Allow block in "root" (DIV)
-		modelDoc.schema.allow( { name: '$block', inside: '$root' } );
-
-		// Bold text is allowed only in P.
-		modelDoc.schema.allow( { name: '$text', attributes: 'bold', inside: 'p' } );
-		modelDoc.schema.allow( { name: 'p', attributes: 'bold', inside: '$root' } );
-
-		// Disallow bold on image.
-		modelDoc.schema.disallow( { name: 'img', attributes: 'bold', inside: '$root' } );
-	} );
-
-	afterEach( () => {
-		command.destroy();
-	} );
-
-	describe( 'value', () => {
-		it( 'is true when selection has the attribute', () => {
-			modelDoc.enqueueChanges( () => {
-				modelDoc.selection.setAttribute( attrKey, true );
-			} );
-
-			expect( command.value ).to.be.true;
-		} );
-
-		it( 'is false when selection does not have the attribute', () => {
-			modelDoc.enqueueChanges( () => {
-				modelDoc.selection.removeAttribute( attrKey );
-			} );
-
-			expect( command.value ).to.be.false;
-		} );
-	} );
-
-	describe( 'isEnabled', () => {
-		// This test doesn't tests every possible case.
-		// Method `refresh()` uses `isAttributeAllowedInSelection` helper which is fully tested in his own test.
-
-		beforeEach( () => {
-			modelDoc.schema.registerItem( 'x', '$block' );
-			modelDoc.schema.disallow( { name: '$text', inside: 'x', attributes: 'link' } );
-		} );
-
-		describe( 'when selection is collapsed', () => {
-			it( 'should return true if characters with the attribute can be placed at caret position', () => {
-				setData( modelDoc, '<p>f[]oo</p>' );
-				expect( command.isEnabled ).to.be.true;
-			} );
-
-			it( 'should return false if characters with the attribute cannot be placed at caret position', () => {
-				setData( modelDoc, '<x>fo[]o</x>' );
-				expect( command.isEnabled ).to.be.false;
-			} );
-		} );
-
-		describe( 'when selection is not collapsed', () => {
-			it( 'should return true if there is at least one node in selection that can have the attribute', () => {
-				setData( modelDoc, '<p>[foo]</p>' );
-				expect( command.isEnabled ).to.be.true;
-			} );
-
-			it( 'should return false if there are no nodes in selection that can have the attribute', () => {
-				setData( modelDoc, '<x>[foo]</x>' );
-				expect( command.isEnabled ).to.be.false;
-			} );
-		} );
-	} );
-
-	describe( 'execute()', () => {
-		it( 'should do nothing if the command is disabled', () => {
-			setData( modelDoc, '<p>fo[ob]ar</p>' );
-
-			command.isEnabled = false;
-
-			command.execute();
-
-			expect( getData( modelDoc ) ).to.equal( '<p>fo[ob]ar</p>' );
-		} );
-
-		it( 'should add attribute on selected nodes if the command value was false', () => {
-			setData( modelDoc, '<p>a[bc<$text bold="true">fo]obar</$text>xyz</p>' );
-
-			expect( command.value ).to.be.false;
-
-			command.execute();
-
-			expect( command.value ).to.be.true;
-			expect( getData( modelDoc ) ).to.equal( '<p>a[<$text bold="true">bcfo]obar</$text>xyz</p>' );
-		} );
-
-		it( 'should remove attribute from selected nodes if the command value was true', () => {
-			setData( modelDoc, '<p>abc[<$text bold="true">foo]bar</$text>xyz</p>' );
-
-			expect( command.value ).to.be.true;
-
-			command.execute();
-
-			expect( getData( modelDoc ) ).to.equal( '<p>abc[foo]<$text bold="true">bar</$text>xyz</p>' );
-			expect( command.value ).to.be.false;
-		} );
-
-		it( 'should add attribute on selected nodes if execute parameter was set to true', () => {
-			setData( modelDoc, '<p>abc<$text bold="true">foob[ar</$text>x]yz</p>' );
-
-			expect( command.value ).to.be.true;
-
-			command.execute( { forceValue: true } );
-
-			expect( command.value ).to.be.true;
-			expect( getData( modelDoc ) ).to.equal( '<p>abc<$text bold="true">foob[arx</$text>]yz</p>' );
-		} );
-
-		it( 'should remove attribute on selected nodes if execute parameter was set to false', () => {
-			setData( modelDoc, '<p>a[bc<$text bold="true">fo]obar</$text>xyz</p>' );
-
-			command.execute( { forceValue: false } );
-
-			expect( command.value ).to.be.false;
-			expect( getData( modelDoc ) ).to.equal( '<p>a[bcfo]<$text bold="true">obar</$text>xyz</p>' );
-		} );
-
-		it( 'should change selection attribute if selection is collapsed in non-empty parent', () => {
-			setData( modelDoc, '<p>a[]bc<$text bold="true">foobar</$text>xyz</p><p></p>' );
-
-			expect( command.value ).to.be.false;
-
-			command.execute();
-
-			expect( command.value ).to.be.true;
-			expect( modelDoc.selection.hasAttribute( 'bold' ) ).to.be.true;
-
-			command.execute();
-
-			expect( command.value ).to.be.false;
-			expect( modelDoc.selection.hasAttribute( 'bold' ) ).to.be.false;
-		} );
-
-		it( 'should not store attribute change on selection if selection is collapsed in non-empty parent', () => {
-			setData( modelDoc, '<p>a[]bc<$text bold="true">foobar</$text>xyz</p>' );
-
-			command.execute();
-
-			// It should not save that bold was executed at position ( root, [ 0, 1 ] ).
-
-			modelDoc.enqueueChanges( () => {
-				// Simulate clicking right arrow key by changing selection ranges.
-				modelDoc.selection.setRanges( [ new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 2 ] ) ) ] );
-
-				// Get back to previous selection.
-				modelDoc.selection.setRanges( [ new Range( new Position( root, [ 0, 1 ] ), new Position( root, [ 0, 1 ] ) ) ] );
-			} );
-
-			expect( command.value ).to.be.false;
-		} );
-
-		it( 'should change selection attribute and store it if selection is collapsed in empty parent', () => {
-			setData( modelDoc, '<p>abc<$text bold="true">foobar</$text>xyz</p><p>[]</p>' );
-
-			expect( command.value ).to.be.false;
-
-			command.execute();
-
-			expect( command.value ).to.be.true;
-			expect( modelDoc.selection.hasAttribute( 'bold' ) ).to.be.true;
-
-			// Attribute should be stored.
-			// Simulate clicking somewhere else in the editor.
-			modelDoc.enqueueChanges( () => {
-				modelDoc.selection.setRanges( [ new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 2 ] ) ) ] );
-			} );
-
-			expect( command.value ).to.be.false;
-
-			// Go back to where attribute was stored.
-			modelDoc.enqueueChanges( () => {
-				modelDoc.selection.setRanges( [ new Range( new Position( root, [ 1, 0 ] ), new Position( root, [ 1, 0 ] ) ) ] );
-			} );
-
-			// Attribute should be restored.
-			expect( command.value ).to.be.true;
-
-			command.execute();
-
-			expect( command.value ).to.be.false;
-			expect( modelDoc.selection.hasAttribute( 'bold' ) ).to.be.false;
-		} );
-
-		it( 'should not apply attribute change where it would invalid schema', () => {
-			modelDoc.schema.registerItem( 'image', '$block' );
-			setData( modelDoc, '<p>ab[c<img></img><$text bold="true">foobar</$text>xy<img></img>]z</p>' );
-
-			expect( command.isEnabled ).to.be.true;
-
-			command.execute();
-
-			expect( getData( modelDoc ) )
-				.to.equal( '<p>ab[<$text bold="true">c</$text><img></img><$text bold="true">foobarxy</$text><img></img>]z</p>' );
-		} );
-
-		it( 'should use provided batch for storing undo steps', () => {
-			const batch = new Batch( new Document() );
-			setData( modelDoc, '<p>a[bc<$text bold="true">fo]obar</$text>xyz</p>' );
-
-			expect( batch.deltas.length ).to.equal( 0 );
-
-			command.execute( { batch } );
-
-			expect( batch.deltas.length ).to.equal( 1 );
-			expect( getData( modelDoc ) ).to.equal( '<p>a[<$text bold="true">bcfo]obar</$text>xyz</p>' );
-		} );
-
-		describe( 'should cause firing model document changesDone event', () => {
-			let spy;
-
-			beforeEach( () => {
-				spy = sinon.spy();
-			} );
-
-			it( 'collapsed selection in non-empty parent', () => {
-				setData( modelDoc, '<p>x[]y</p>' );
-
-				modelDoc.on( 'changesDone', spy );
-
-				command.execute();
-
-				expect( spy.calledOnce ).to.be.true;
-			} );
-
-			it( 'non-collapsed selection', () => {
-				setData( modelDoc, '<p>[xy]</p>' );
-
-				modelDoc.on( 'changesDone', spy );
-
-				command.execute();
-
-				expect( spy.calledOnce ).to.be.true;
-			} );
-
-			it( 'in empty parent', () => {
-				setData( modelDoc, '<p>[]</p>' );
-
-				modelDoc.on( 'changesDone', spy );
-
-				command.execute();
-
-				expect( spy.calledOnce ).to.be.true;
-			} );
-		} );
-	} );
-} );