Browse Source

Adjusted tests to refactored model utils.

Oskar Wrobel 9 years ago
parent
commit
5a475d1103

+ 90 - 81
packages/ckeditor5-engine/tests/_utils-tests/model.js

@@ -20,6 +20,15 @@ describe( 'model test utils', () => {
 		selection = document.selection;
 		selection = document.selection;
 		sandbox = sinon.sandbox.create();
 		sandbox = sinon.sandbox.create();
 		selection.removeAllRanges();
 		selection.removeAllRanges();
+
+		document.schema.registerItem( 'a', '$inline' );
+		document.schema.allow( { name: 'a', inside: '$root' } );
+		document.schema.registerItem( 'b', '$inline' );
+		document.schema.allow( { name: 'b', inside: '$root' } );
+		document.schema.registerItem( 'c', '$inline' );
+		document.schema.allow( { name: 'c', inside: '$root' } );
+		document.schema.registerItem( 'paragraph', '$block' );
+		document.schema.allow( { name: '$text', inside: '$root' } );
 	} );
 	} );
 
 
 	afterEach( () => {
 	afterEach( () => {
@@ -41,7 +50,7 @@ describe( 'model test utils', () => {
 			root.appendChildren( new Element( 'b', null, new Text( 'btext' ) ) );
 			root.appendChildren( new Element( 'b', null, new Text( 'btext' ) ) );
 			document.selection.addRange( Range.createFromParentsAndOffsets( root, 0, root, 1 ) );
 			document.selection.addRange( Range.createFromParentsAndOffsets( root, 0, root, 1 ) );
 
 
-			expect( getData( document ) ).to.equal( '<selection><b>btext</b></selection>' );
+			expect( getData( document ) ).to.equal( '[<b>btext</b>]' );
 			sinon.assert.calledOnce( stringifySpy );
 			sinon.assert.calledOnce( stringifySpy );
 			sinon.assert.calledWithExactly( stringifySpy, root, document.selection );
 			sinon.assert.calledWithExactly( stringifySpy, root, document.selection );
 		} );
 		} );
@@ -50,7 +59,7 @@ describe( 'model test utils', () => {
 			root.appendChildren( 'நிலைக்கு' );
 			root.appendChildren( 'நிலைக்கு' );
 			document.selection.addRange( Range.createFromParentsAndOffsets( root, 2, root, 6 ) );
 			document.selection.addRange( Range.createFromParentsAndOffsets( root, 2, root, 6 ) );
 
 
-			expect( getData( document ) ).to.equal( 'நி<selection>லைக்</selection>கு' );
+			expect( getData( document ) ).to.equal( 'நி[லைக்]கு' );
 		} );
 		} );
 
 
 		it( 'should throw an error when passing invalid document', () => {
 		it( 'should throw an error when passing invalid document', () => {
@@ -65,7 +74,6 @@ describe( 'model test utils', () => {
 			const parseSpy = sandbox.spy( setData, '_parse' );
 			const parseSpy = sandbox.spy( setData, '_parse' );
 			const options = {};
 			const options = {};
 			const data = '<b>btext</b>text';
 			const data = '<b>btext</b>text';
-			document.schema.registerItem( 'b', '$inline' );
 
 
 			setData( document, data, options );
 			setData( document, data, options );
 
 
@@ -79,7 +87,6 @@ describe( 'model test utils', () => {
 			const parseSpy = sandbox.spy( setData, '_parse' );
 			const parseSpy = sandbox.spy( setData, '_parse' );
 			const options = {};
 			const options = {};
 			const data = '[<b>btext</b>]';
 			const data = '[<b>btext</b>]';
-			document.schema.registerItem( 'b', '$inline' );
 
 
 			setData( document, data, options );
 			setData( document, data, options );
 
 
@@ -90,52 +97,50 @@ describe( 'model test utils', () => {
 		} );
 		} );
 
 
 		it( 'should insert text', () => {
 		it( 'should insert text', () => {
-			test( 'this is test text', '<selection />this is test text' );
+			test( 'this is test text', '[]this is test text' );
 		} );
 		} );
 
 
 		it( 'should insert text with selection around', () => {
 		it( 'should insert text with selection around', () => {
-			test( '<selection>this is test text</selection>' );
+			test( '[this is test text]' );
 		} );
 		} );
 
 
 		it( 'should insert text with selection inside #1', () => {
 		it( 'should insert text with selection inside #1', () => {
-			test( 'this <selection>is test</selection> text' );
+			test( 'this [is test] text' );
 		} );
 		} );
 
 
 		it( 'should insert text with selection inside #2', () => {
 		it( 'should insert text with selection inside #2', () => {
-			test( '<selection>this is test</selection> text' );
+			test( '[this is test] text' );
 		} );
 		} );
 
 
 		it( 'should insert text with selection inside #2', () => {
 		it( 'should insert text with selection inside #2', () => {
-			test( 'this is <selection>test text</selection>' );
+			test( 'this is [test text]' );
 		} );
 		} );
 
 
 		it( 'should insert element', () => {
 		it( 'should insert element', () => {
-			document.schema.registerItem( 'b', '$inline' );
-			test( '<b>foo bar</b>', '<selection /><b>foo bar</b>' );
+			test( '<b>foo bar</b>', '[]<b>foo bar</b>' );
 		} );
 		} );
 
 
 		it( 'should insert element with selection inside #1', () => {
 		it( 'should insert element with selection inside #1', () => {
-			document.schema.registerItem( 'b', '$inline' );
-			test( '<b><selection>foo </selection>bar</b>' );
+			test( '<b>[foo ]bar</b>' );
 		} );
 		} );
 
 
 		it( 'should insert element with selection inside #2', () => {
 		it( 'should insert element with selection inside #2', () => {
-			document.schema.registerItem( 'b', '$inline' );
-			test( '<selection><b>foo </selection>bar</b>' );
+			test( '[<b>foo ]bar</b>' );
 		} );
 		} );
 
 
 		it( 'should insert element with selection inside #3', () => {
 		it( 'should insert element with selection inside #3', () => {
-			document.schema.registerItem( 'b', '$inline' );
-			test( '<b><selection>foo bar</b></selection>' );
+			test( '<b>[foo bar</b>]' );
 		} );
 		} );
 
 
 		it( 'should insert backward selection', () => {
 		it( 'should insert backward selection', () => {
-			document.schema.registerItem( 'b', '$inline' );
-			test( '<b><selection backward>foo bar</b></selection>' );
+			setData( document, '<b>[foo bar</b>]', { lastRangeBackward: true } );
+
+			expect( getData( document ) ).to.equal( '<b>[foo bar</b>]' );
+			expect( document.selection.isBackward ).to.true;
 		} );
 		} );
 
 
 		it( 'should support unicode', () => {
 		it( 'should support unicode', () => {
-			test( 'நி<selection>லைக்</selection>கு' );
+			test( 'நி[லைக்]கு' );
 		} );
 		} );
 
 
 		it( 'should throw an error when passing invalid document', () => {
 		it( 'should throw an error when passing invalid document', () => {
@@ -195,14 +200,14 @@ describe( 'model test utils', () => {
 		it( 'writes element attributes', () => {
 		it( 'writes element attributes', () => {
 			root.appendChildren(
 			root.appendChildren(
 				new Element( 'a', { foo: true, bar: 1, car: false }, [
 				new Element( 'a', { foo: true, bar: 1, car: false }, [
-					new Element( 'b', { fooBar: 'x y', barFoo: { x: 1, y: 2 } } )
+					new Element( 'b', { fooBar: 'x y', barFoo: JSON.stringify( { x: 1, y: 2 } ) } )
 				] )
 				] )
 			);
 			);
 
 
 			// Note: attributes are written in a very simplistic way, because they are not to be parsed. They are just
 			// Note: attributes are written in a very simplistic way, because they are not to be parsed. They are just
 			// to be compared in the tests with some patterns.
 			// to be compared in the tests with some patterns.
 			expect( stringify( root ) ).to.equal(
 			expect( stringify( root ) ).to.equal(
-				'<a bar=1 car=false foo=true><b barFoo={"x":1,"y":2} fooBar="x y"></b></a>'
+				'<a bar="1" car="false" foo="true"><b barFoo="{"x":1,"y":2}" fooBar="x y"></b></a>'
 			);
 			);
 		} );
 		} );
 
 
@@ -219,7 +224,7 @@ describe( 'model test utils', () => {
 			expect( stringify( root ) ).to.equal(
 			expect( stringify( root ) ).to.equal(
 				'<$text bold="true">foo</$text>' +
 				'<$text bold="true">foo</$text>' +
 				'bar' +
 				'bar' +
-				'<$text bold="true" italic=true>bom</$text>' +
+				'<$text bold="true" italic="true">bom</$text>' +
 				'<a><$text bold="true" underline="true">pom</$text></a>'
 				'<a><$text bold="true" underline="true">pom</$text></a>'
 			);
 			);
 		} );
 		} );
@@ -244,7 +249,7 @@ describe( 'model test utils', () => {
 				selection.collapse( root );
 				selection.collapse( root );
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<selection />'
+					'[]'
 				);
 				);
 			} );
 			} );
 
 
@@ -256,7 +261,7 @@ describe( 'model test utils', () => {
 				selection.collapse( root );
 				selection.collapse( root );
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<selection /><a></a>foo<$text bold=true>bar</$text><b></b>'
+					'[]<a></a>foo<$text bold="true">bar</$text><b></b>'
 				);
 				);
 			} );
 			} );
 
 
@@ -264,7 +269,7 @@ describe( 'model test utils', () => {
 				selection.collapse( root, 3 );
 				selection.collapse( root, 3 );
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<a></a>fo<selection />o<$text bold=true>bar</$text><b></b>'
+					'<a></a>fo[]o<$text bold="true">bar</$text><b></b>'
 				);
 				);
 			} );
 			} );
 
 
@@ -272,7 +277,7 @@ describe( 'model test utils', () => {
 				selection.collapse( elA, 'after' );
 				selection.collapse( elA, 'after' );
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<a></a><selection />foo<$text bold=true>bar</$text><b></b>'
+					'<a></a>[]foo<$text bold="true">bar</$text><b></b>'
 				);
 				);
 			} );
 			} );
 
 
@@ -280,8 +285,9 @@ describe( 'model test utils', () => {
 				selection.collapse( elB, 'before' );
 				selection.collapse( elB, 'before' );
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<a></a>foo<$text bold=true>bar</$text><selection bold=true /><b></b>'
+					'<a></a>foo<$text bold="true">bar</$text>[]<b></b>'
 				);
 				);
+				expect( selection.getAttribute( 'bold' ) ).to.true;
 			} );
 			} );
 
 
 			it( 'writes selection collapsed at the end of the root', () => {
 			it( 'writes selection collapsed at the end of the root', () => {
@@ -291,16 +297,7 @@ describe( 'model test utils', () => {
 				selection.clearAttributes();
 				selection.clearAttributes();
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<a></a>foo<$text bold=true>bar</$text><b></b><selection />'
-				);
-			} );
-
-			it( 'writes selection attributes', () => {
-				selection.collapse( root );
-				selection.setAttributesTo( { italic: true, bold: true } );
-
-				expect( stringify( root, selection )  ).to.equal(
-					'<selection bold=true italic=true /><a></a>foo<$text bold=true>bar</$text><b></b>'
+					'<a></a>foo<$text bold="true">bar</$text><b></b>[]'
 				);
 				);
 			} );
 			} );
 
 
@@ -308,8 +305,9 @@ describe( 'model test utils', () => {
 				selection.collapse( root, 5 );
 				selection.collapse( root, 5 );
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<a></a>foo<$text bold=true>b<selection bold=true />ar</$text><b></b>'
+					'<a></a>foo<$text bold="true">b[]ar</$text><b></b>'
 				);
 				);
+				expect( selection.getAttribute( 'bold' ) ).to.true;
 			} );
 			} );
 
 
 			it( 'writes flat selection containing couple of nodes', () => {
 			it( 'writes flat selection containing couple of nodes', () => {
@@ -318,7 +316,7 @@ describe( 'model test utils', () => {
 				);
 				);
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<selection><a></a>foo</selection><$text bold=true>bar</$text><b></b>'
+					'[<a></a>foo]<$text bold="true">bar</$text><b></b>'
 				);
 				);
 			} );
 			} );
 
 
@@ -328,7 +326,7 @@ describe( 'model test utils', () => {
 				);
 				);
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<a></a>f<selection>o</selection>o<$text bold=true>bar</$text><b></b>'
+					'<a></a>f[o]o<$text bold="true">bar</$text><b></b>'
 				);
 				);
 			} );
 			} );
 
 
@@ -338,18 +336,18 @@ describe( 'model test utils', () => {
 				);
 				);
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<a><selection></a>foo<$text bold=true>bar</$text><b></selection></b>'
+					'<a>[</a>foo<$text bold="true">bar</$text><b>]</b>'
 				);
 				);
 			} );
 			} );
 
 
-			it( 'writes backward selection', () => {
+			it( 'writes selection when is backward', () => {
 				selection.addRange(
 				selection.addRange(
 					Range.createFromParentsAndOffsets( elA, 0, elB, 0 ),
 					Range.createFromParentsAndOffsets( elA, 0, elB, 0 ),
 					true
 					true
 				);
 				);
 
 
 				expect( stringify( root, selection ) ).to.equal(
 				expect( stringify( root, selection ) ).to.equal(
-					'<a><selection backward></a>foo<$text bold=true>bar</$text><b></selection></b>'
+					'<a>[</a>foo<$text bold="true">bar</$text><b>]</b>'
 				);
 				);
 			} );
 			} );
 
 
@@ -357,7 +355,7 @@ describe( 'model test utils', () => {
 				const range = Range.createFromParentsAndOffsets( elA, 0, elB, 0 );
 				const range = Range.createFromParentsAndOffsets( elA, 0, elB, 0 );
 
 
 				expect( stringify( root, range ) ).to.equal(
 				expect( stringify( root, range ) ).to.equal(
-					'<a><selection></a>foo<$text bold=true>bar</$text><b></selection></b>'
+					'<a>[</a>foo<$text bold="true">bar</$text><b>]</b>'
 				);
 				);
 			} );
 			} );
 
 
@@ -365,7 +363,7 @@ describe( 'model test utils', () => {
 				const position = new Position( root, [ 0 ] );
 				const position = new Position( root, [ 0 ] );
 
 
 				expect( stringify( root, position ) ).to.equal(
 				expect( stringify( root, position ) ).to.equal(
-					'<selection /><a></a>foo<$text bold=true>bar</$text><b></b>'
+					'[]<a></a>foo<$text bold="true">bar</$text><b></b>'
 				);
 				);
 			} );
 			} );
 		} );
 		} );
@@ -380,7 +378,7 @@ describe( 'model test utils', () => {
 		} );
 		} );
 
 
 		test( 'creates empty DocumentFragment with selection', {
 		test( 'creates empty DocumentFragment with selection', {
-			data: '<selection />',
+			data: '[]',
 			check( fragment, selection ) {
 			check( fragment, selection ) {
 				expect( fragment ).to.be.instanceOf( DocumentFragment );
 				expect( fragment ).to.be.instanceOf( DocumentFragment );
 				expect( fragment.childCount ).to.equal( 0 );
 				expect( fragment.childCount ).to.equal( 0 );
@@ -390,7 +388,7 @@ describe( 'model test utils', () => {
 		} );
 		} );
 
 
 		test( 'returns Element if range is around single element', {
 		test( 'returns Element if range is around single element', {
-			data: '<selection><a></a></selection>',
+			data: '[<a></a>]',
 			check( el, selection ) {
 			check( el, selection ) {
 				const fragment = el.parent;
 				const fragment = el.parent;
 				expect( el ).to.be.instanceOf( Element );
 				expect( el ).to.be.instanceOf( Element );
@@ -424,23 +422,15 @@ describe( 'model test utils', () => {
 			}
 			}
 		} );
 		} );
 
 
-		// test( 'sets complex attributes', {
-		// 	data: `<a foo='{"a":1,"b":"c"}'></a>`,
-		// 	check( a ) {
-		// 		console.log( JSON.parse( a.getAttribute( 'foo' ) ) );
-		// 		expect( JSON.parse( a.getAttribute( 'foo' ) ) ).to.have.property( 'a', 1 );
-		// 	}
-		// } );
-
 		test( 'sets text attributes', {
 		test( 'sets text attributes', {
-			data: '<$text bold=true italic=true>foo</$text><$text bold=true>bar</$text>bom',
+			data: '<$text bold="true" italic="true">foo</$text><$text bold="true">bar</$text>bom',
 			check( root ) {
 			check( root ) {
 				expect( root.childCount ).to.equal( 3 );
 				expect( root.childCount ).to.equal( 3 );
 				expect( root.maxOffset ).to.equal( 9 );
 				expect( root.maxOffset ).to.equal( 9 );
 				expect( root.getChild( 0 ) ).to.have.property( 'data', 'foo' );
 				expect( root.getChild( 0 ) ).to.have.property( 'data', 'foo' );
-				expect( root.getChild( 0 ).getAttribute( 'italic' ) ).to.equal( true );
+				expect( root.getChild( 0 ).getAttribute( 'italic' ) ).to.equal( 'true' );
 				expect( root.getChild( 1 ) ).to.have.property( 'data', 'bar' );
 				expect( root.getChild( 1 ) ).to.have.property( 'data', 'bar' );
-				expect( root.getChild( 1 ).getAttribute( 'bold' ) ).to.equal( true );
+				expect( root.getChild( 1 ).getAttribute( 'bold' ) ).to.equal( 'true' );
 			}
 			}
 		} );
 		} );
 
 
@@ -496,26 +486,26 @@ describe( 'model test utils', () => {
 
 
 		describe( 'selection', () => {
 		describe( 'selection', () => {
 			test( 'sets collapsed selection in an element', {
 			test( 'sets collapsed selection in an element', {
-				data: '<a><selection /></a>',
+				data: '<a>[]</a>',
 				check( root, selection ) {
 				check( root, selection ) {
 					expect( selection.getFirstPosition().parent ).to.have.property( 'name', 'a' );
 					expect( selection.getFirstPosition().parent ).to.have.property( 'name', 'a' );
 				}
 				}
 			} );
 			} );
 
 
 			test( 'sets collapsed selection between elements', {
 			test( 'sets collapsed selection between elements', {
-				data: '<a></a><selection /><b></b>'
+				data: '<a></a>[]<b></b>'
 			} );
 			} );
 
 
 			test( 'sets collapsed selection before a text', {
 			test( 'sets collapsed selection before a text', {
-				data: '<a></a><selection />foo'
+				data: '<a></a>[]foo'
 			} );
 			} );
 
 
 			test( 'sets collapsed selection after a text', {
 			test( 'sets collapsed selection after a text', {
-				data: 'foo<selection />'
+				data: 'foo[]'
 			} );
 			} );
 
 
 			test( 'sets collapsed selection within a text', {
 			test( 'sets collapsed selection within a text', {
-				data: 'foo<selection />bar',
+				data: 'foo[]bar',
 				check( text, selection ) {
 				check( text, selection ) {
 					expect( text.offsetSize ).to.equal( 6 );
 					expect( text.offsetSize ).to.equal( 6 );
 					expect( text.getPath() ).to.deep.equal( [ 0 ] );
 					expect( text.getPath() ).to.deep.equal( [ 0 ] );
@@ -524,15 +514,19 @@ describe( 'model test utils', () => {
 				}
 				}
 			} );
 			} );
 
 
-			test( 'sets selection attributes', {
-				data: 'foo<selection bold="true" italic="true" />bar',
-				check( root, selection ) {
-					expect( selection.getAttribute( 'italic' ) ).to.be.true;
-				}
+			it( 'sets selection attributes', () => {
+				const result = parse( 'foo[]bar', document.schema, { selectionAttributes: {
+					bold: true,
+					italic: true
+				} } );
+
+				expect( stringify( result.model, result.selection ) ).to.equal( 'foo[]bar' );
+				expect( result.selection.getAttribute( 'italic' ) ).to.be.true;
+				expect( result.selection.getAttribute( 'bold' ) ).to.be.true;
 			} );
 			} );
 
 
 			test( 'sets collapsed selection between text and text with attributes', {
 			test( 'sets collapsed selection between text and text with attributes', {
-				data: 'foo<selection /><$text bold="true">bar</$text>',
+				data: 'foo[]<$text bold="true">bar</$text>',
 				check( root, selection ) {
 				check( root, selection ) {
 					expect( root.maxOffset ).to.equal( 6 );
 					expect( root.maxOffset ).to.equal( 6 );
 					expect( selection.getAttribute( 'bold' ) ).to.be.undefined;
 					expect( selection.getAttribute( 'bold' ) ).to.be.undefined;
@@ -540,34 +534,49 @@ describe( 'model test utils', () => {
 			} );
 			} );
 
 
 			test( 'sets selection containing an element', {
 			test( 'sets selection containing an element', {
-				data: 'x<selection><a></a></selection>'
+				data: 'x[<a></a>]'
 			} );
 			} );
 
 
-			test( 'sets selection with attribute containing an element', {
-				data: 'x<selection bold="true"><a></a></selection>'
+			it( 'sets selection with attribute containing an element', () => {
+				const result = parse( 'x[<a></a>]', document.schema, { selectionAttributes: {
+					bold: true
+				} } );
+
+				expect( stringify( result.model, result.selection ) ).to.equal( 'x[<a></a>]' );
+				expect( result.selection.getAttribute( 'bold' ) ).to.be.true;
 			} );
 			} );
 
 
-			test( 'sets a backward selection containing an element', {
-				data: 'x<selection backward bold="true"><a></a></selection>'
+			it( 'sets a backward selection containing an element', () => {
+				const result = parse( 'x[<a></a>]', document.schema, {
+					lastRangeBackward: true
+				} );
+
+				expect( stringify( result.model, result.selection ) ).to.equal( 'x[<a></a>]' );
+				expect( result.selection.isBackward ).to.true;
 			} );
 			} );
 
 
 			test( 'sets selection within a text', {
 			test( 'sets selection within a text', {
-				data: 'x<selection bold="true">y</selection>z'
+				data: 'x[y]z'
 			} );
 			} );
 
 
-			test( 'sets selection within a text with different attributes', {
-				data: '<model-text bold="true">fo<selection bold="true">o</model-text>ba</selection>r'
+			it( 'sets a backward selection containing an element', () => {
+				const result = parse( '<$text bold="true">fo[o</$text>ba]r', document.schema, {
+					selectionAttributes: { bold: true }
+				} );
+
+				expect( stringify( result.model, result.selection ) ).to.equal( '<$text bold="true">fo[o</$text>ba]r' );
+				expect( result.selection.getAttribute( 'bold' ) ).to.true;
 			} );
 			} );
 
 
 			it( 'throws when missing selection start', () => {
 			it( 'throws when missing selection start', () => {
 				expect( () => {
 				expect( () => {
-					parse( 'foo</selection>' );
+					parse( 'foo]' );
 				} ).to.throw( Error );
 				} ).to.throw( Error );
 			} );
 			} );
 
 
 			it( 'throws when missing selection end', () => {
 			it( 'throws when missing selection end', () => {
 				expect( () => {
 				expect( () => {
-					parse( '<selection>foo' );
+					parse( '[foo' );
 				} ).to.throw( Error );
 				} ).to.throw( Error );
 			} );
 			} );
 		} );
 		} );
@@ -575,7 +584,7 @@ describe( 'model test utils', () => {
 		function test( title, options ) {
 		function test( title, options ) {
 			it( title, () => {
 			it( title, () => {
 				const output = options.output || options.data;
 				const output = options.output || options.data;
-				const data = parse( options.data );
+				const data = parse( options.data, document.schema );
 				let model, selection;
 				let model, selection;
 
 
 				if ( data.selection && data.model ) {
 				if ( data.selection && data.model ) {

+ 1 - 1
packages/ckeditor5-engine/tests/conversion/model-selection-to-view-converters.js

@@ -217,7 +217,7 @@ describe( 'default converters', () => {
 			// Similar test case as above
 			// Similar test case as above
 			test(
 			test(
 				[ 3, 3 ],
 				[ 3, 3 ],
-				'f<$text bold=true>ooba</$text>r',
+				'f<$text bold="true">ooba</$text>r',
 				'f<strong>ooba</strong>r' // No selection in view.
 				'f<strong>ooba</strong>r' // No selection in view.
 			);
 			);
 		} );
 		} );

+ 6 - 5
packages/ckeditor5-engine/tests/conversion/view-selection-to-model-converters.js

@@ -48,7 +48,7 @@ describe( 'convertSelectionChange', () => {
 		convertSelection( null, { newSelection: viewSelection } );
 		convertSelection( null, { newSelection: viewSelection } );
 
 
 		expect( modelGetData( model ) ).to.equals( '<paragraph>f[]oo</paragraph><paragraph>bar</paragraph>' );
 		expect( modelGetData( model ) ).to.equals( '<paragraph>f[]oo</paragraph><paragraph>bar</paragraph>' );
-		expect( modelGetData( model ) ).to.equal( '<paragraph>f<selection />oo</paragraph><paragraph>bar</paragraph>' );
+		expect( modelGetData( model ) ).to.equal( '<paragraph>f[]oo</paragraph><paragraph>bar</paragraph>' );
 	} );
 	} );
 
 
 	it( 'should support unicode', () => {
 	it( 'should support unicode', () => {
@@ -65,7 +65,7 @@ describe( 'convertSelectionChange', () => {
 
 
 		convertSelection( null, { newSelection: viewSelection } );
 		convertSelection( null, { newSelection: viewSelection } );
 
 
-		expect( modelGetData( model ) ).to.equal( '<paragraph>நி<selection>லைக்</selection>கு</paragraph>' );
+		expect( modelGetData( model ) ).to.equal( '<paragraph>நி[லைக்]கு</paragraph>' );
 	} );
 	} );
 
 
 	it( 'should convert multi ranges selection', () => {
 	it( 'should convert multi ranges selection', () => {
@@ -98,11 +98,12 @@ describe( 'convertSelectionChange', () => {
 		const viewSelection = new ViewSelection();
 		const viewSelection = new ViewSelection();
 		viewSelection.addRange( ViewRange.createFromParentsAndOffsets(
 		viewSelection.addRange( ViewRange.createFromParentsAndOffsets(
 			viewRoot.getChild( 0 ).getChild( 0 ), 1, viewRoot.getChild( 0 ).getChild( 0 ), 2 ), true );
 			viewRoot.getChild( 0 ).getChild( 0 ), 1, viewRoot.getChild( 0 ).getChild( 0 ), 2 ), true );
+		viewSelection.addRange( ViewRange.createFromParentsAndOffsets(
+			viewRoot.getChild( 1 ).getChild( 0 ), 1, viewRoot.getChild( 1 ).getChild( 0 ), 2 ), true );
 
 
 		convertSelection( null, { newSelection: viewSelection } );
 		convertSelection( null, { newSelection: viewSelection } );
 
 
-		expect( modelGetData( model ) ).to.equal(
-			'<paragraph>f[o]o</paragraph><paragraph>b[a]r</paragraph>' );
-		expect( model.selection.isBackward() ).to.true;
+		expect( modelGetData( model ) ).to.equal( '<paragraph>f[o]o</paragraph><paragraph>b[a]r</paragraph>' );
+		expect( model.selection.isBackward ).to.true;
 	} );
 	} );
 } );
 } );

+ 4 - 3
packages/ckeditor5-engine/tests/model/composer/composer.js

@@ -28,7 +28,7 @@ describe( 'Composer', () => {
 
 
 			composer.fire( 'deleteContents', { batch, selection: document.selection } );
 			composer.fire( 'deleteContents', { batch, selection: document.selection } );
 
 
-			expect( getData( document ) ).to.equal( '<p>f<selection /></p><p>r</p>' );
+			expect( getData( document ) ).to.equal( '<p>f[]</p><p>r</p>' );
 			expect( batch.deltas ).to.not.be.empty;
 			expect( batch.deltas ).to.not.be.empty;
 		} );
 		} );
 
 
@@ -43,7 +43,7 @@ describe( 'Composer', () => {
 				options: { merge: true }
 				options: { merge: true }
 			} );
 			} );
 
 
-			expect( getData( document ) ).to.equal( '<p>f<selection />r</p>' );
+			expect( getData( document ) ).to.equal( '<p>f[]r</p>' );
 		} );
 		} );
 
 
 		it( 'attaches modifySelection default listener', () => {
 		it( 'attaches modifySelection default listener', () => {
@@ -57,7 +57,8 @@ describe( 'Composer', () => {
 			} );
 			} );
 
 
 			expect( getData( document ) )
 			expect( getData( document ) )
-				.to.equal( '<p>fo<selection backward>o</selection>bar</p>' );
+				.to.equal( '<p>fo[o]bar</p>' );
+			expect( document.selection.isBackward ).to.true;
 		} );
 		} );
 	} );
 	} );
 
 

+ 54 - 52
packages/ckeditor5-engine/tests/model/composer/deletecontents.js

@@ -26,6 +26,8 @@ describe( 'Delete utils', () => {
 		schema.registerItem( 'pchild' );
 		schema.registerItem( 'pchild' );
 
 
 		schema.allow( { name: 'pchild', inside: 'p' } );
 		schema.allow( { name: 'pchild', inside: 'p' } );
+		schema.allow( { name: '$text', inside: '$root' } );
+		schema.allow( { name: 'img', inside: '$root' } );
 		schema.allow( { name: '$text', attributes: [ 'bold', 'italic' ] } );
 		schema.allow( { name: '$text', attributes: [ 'bold', 'italic' ] } );
 		schema.allow( { name: 'p', attributes: [ 'align' ] } );
 		schema.allow( { name: 'p', attributes: [ 'align' ] } );
 	} );
 	} );
@@ -34,50 +36,50 @@ describe( 'Delete utils', () => {
 		describe( 'in simple scenarios', () => {
 		describe( 'in simple scenarios', () => {
 			test(
 			test(
 				'does nothing on collapsed selection',
 				'does nothing on collapsed selection',
-				'f<selection />oo',
-				'f<selection />oo'
+				'f[]oo',
+				'f[]oo'
 			);
 			);
 
 
 			test(
 			test(
 				'deletes single character',
 				'deletes single character',
-				'f<selection>o</selection>o',
-				'f<selection />o'
+				'f[o]o',
+				'f[]o'
 			);
 			);
 
 
 			test(
 			test(
 				'xdeletes single character (backward selection)',
 				'xdeletes single character (backward selection)',
-				'f<selection backward>o</selection>o',
-				'f<selection />o'
+				'f<selection backward>o]o',
+				'f[]o'
 			);
 			);
 
 
 			test(
 			test(
 				'deletes whole text',
 				'deletes whole text',
-				'<selection>foo</selection>',
-				'<selection />'
+				'[foo]',
+				'[]'
 			);
 			);
 
 
 			test(
 			test(
 				'deletes whole text between nodes',
 				'deletes whole text between nodes',
-				'<img></img><selection>foo</selection><img></img>',
-				'<img></img><selection /><img></img>'
+				'<img></img>[foo]<img></img>',
+				'<img></img>[]<img></img>'
 			);
 			);
 
 
 			test(
 			test(
 				'deletes an element',
 				'deletes an element',
-				'x<selection><img></img></selection>y',
-				'x<selection />y'
+				'x[<img></img>]y',
+				'x[]y'
 			);
 			);
 
 
 			test(
 			test(
 				'deletes a bunch of nodes',
 				'deletes a bunch of nodes',
-				'w<selection>x<img></img>y</selection>z',
-				'w<selection />z'
+				'w[x<img></img>y]z',
+				'w[]z'
 			);
 			);
 
 
 			test(
 			test(
 				'does not break things when option.merge passed',
 				'does not break things when option.merge passed',
-				'w<selection>x<img></img>y</selection>z',
-				'w<selection />z',
+				'w[x<img></img>y]z',
+				'w[]z',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 		} );
 		} );
@@ -85,26 +87,26 @@ describe( 'Delete utils', () => {
 		describe( 'with text attributes', () => {
 		describe( 'with text attributes', () => {
 			test(
 			test(
 				'deletes characters (first half has attrs)',
 				'deletes characters (first half has attrs)',
-				'<$text bold=true>fo<selection bold=true>o</$text>b</selection>ar',
-				'<$text bold=true>fo</$text><selection bold=true />ar'
+				'<$text bold="true">fo<selection bold=true>o</$text>b]ar',
+				'<$text bold="true">fo</$text><selection bold=true />ar'
 			);
 			);
 
 
 			test(
 			test(
 				'deletes characters (2nd half has attrs)',
 				'deletes characters (2nd half has attrs)',
-				'fo<selection bold=true>o<$text bold=true>b</selection>ar</$text>',
-				'fo<selection /><$text bold=true>ar</$text>'
+				'fo<selection bold=true>o<$text bold="true">b]ar</$text>',
+				'fo[]<$text bold="true">ar</$text>'
 			);
 			);
 
 
 			test(
 			test(
 				'clears selection attrs when emptied content',
 				'clears selection attrs when emptied content',
-				'<p>x</p><p><selection bold=true><$text bold=true>foo</$text></selection></p><p>y</p>',
-				'<p>x</p><p><selection /></p><p>y</p>'
+				'<p>x</p><p><selection bold=true><$text bold="true">foo</$text>]</p><p>y</p>',
+				'<p>x</p><p>[]</p><p>y</p>'
 			);
 			);
 
 
 			test(
 			test(
 				'leaves selection attributes when text contains them',
 				'leaves selection attributes when text contains them',
-				'<p>x<$text bold=true>a<selection bold=true>foo</selection>b</$text>y</p>',
-				'<p>x<$text bold=true>a<selection bold=true />b</$text>y</p>'
+				'<p>x<$text bold="true">a<selection bold=true>foo]b</$text>y</p>',
+				'<p>x<$text bold="true">a<selection bold=true />b</$text>y</p>'
 			);
 			);
 		} );
 		} );
 
 
@@ -120,101 +122,101 @@ describe( 'Delete utils', () => {
 		describe( 'in multi-element scenarios', () => {
 		describe( 'in multi-element scenarios', () => {
 			test(
 			test(
 				'do not merge when no need to',
 				'do not merge when no need to',
-				'<p>x</p><p><selection>foo</selection></p><p>y</p>',
-				'<p>x</p><p><selection /></p><p>y</p>',
+				'<p>x</p><p>[foo]</p><p>y</p>',
+				'<p>x</p><p>[]</p><p>y</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			test(
 			test(
 				'merges second element into the first one (same name)',
 				'merges second element into the first one (same name)',
-				'<p>x</p><p>fo<selection>o</p><p>b</selection>ar</p><p>y</p>',
-				'<p>x</p><p>fo<selection />ar</p><p>y</p>',
+				'<p>x</p><p>fo[o</p><p>b]ar</p><p>y</p>',
+				'<p>x</p><p>fo[]ar</p><p>y</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			test(
 			test(
 				'does not merge second element into the first one (same name, !option.merge)',
 				'does not merge second element into the first one (same name, !option.merge)',
-				'<p>x</p><p>fo<selection>o</p><p>b</selection>ar</p><p>y</p>',
-				'<p>x</p><p>fo<selection /></p><p>ar</p><p>y</p>'
+				'<p>x</p><p>fo[o</p><p>b]ar</p><p>y</p>',
+				'<p>x</p><p>fo[]</p><p>ar</p><p>y</p>'
 			);
 			);
 
 
 			test(
 			test(
 				'merges second element into the first one (same name)',
 				'merges second element into the first one (same name)',
-				'<p>x</p><p>fo<selection>o</p><p>b</selection>ar</p><p>y</p>',
-				'<p>x</p><p>fo<selection />ar</p><p>y</p>',
+				'<p>x</p><p>fo[o</p><p>b]ar</p><p>y</p>',
+				'<p>x</p><p>fo[]ar</p><p>y</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			test(
 			test(
 				'merges second element into the first one (different name)',
 				'merges second element into the first one (different name)',
-				'<p>x</p><h1>fo<selection>o</h1><p>b</selection>ar</p><p>y</p>',
-				'<p>x</p><h1>fo<selection />ar</h1><p>y</p>',
+				'<p>x</p><h1>fo[o</h1><p>b]ar</p><p>y</p>',
+				'<p>x</p><h1>fo[]ar</h1><p>y</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			test(
 			test(
 				'merges second element into the first one (different name, backward selection)',
 				'merges second element into the first one (different name, backward selection)',
-				'<p>x</p><h1>fo<selection backward>o</h1><p>b</selection>ar</p><p>y</p>',
-				'<p>x</p><h1>fo<selection />ar</h1><p>y</p>',
+				'<p>x</p><h1>fo<selection backward>o</h1><p>b]ar</p><p>y</p>',
+				'<p>x</p><h1>fo[]ar</h1><p>y</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			test(
 			test(
 				'merges second element into the first one (different attrs)',
 				'merges second element into the first one (different attrs)',
-				'<p>x</p><p align="l">fo<selection>o</p><p>b</selection>ar</p><p>y</p>',
-				'<p>x</p><p align="l">fo<selection />ar</p><p>y</p>',
+				'<p>x</p><p align="l">fo[o</p><p>b]ar</p><p>y</p>',
+				'<p>x</p><p align="l">fo[]ar</p><p>y</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			test(
 			test(
 				'merges second element to an empty first element',
 				'merges second element to an empty first element',
-				'<p>x</p><h1><selection></h1><p>fo</selection>o</p><p>y</p>',
-				'<p>x</p><h1><selection />o</h1><p>y</p>',
+				'<p>x</p><h1>[</h1><p>fo]o</p><p>y</p>',
+				'<p>x</p><h1>[]o</h1><p>y</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			test(
 			test(
 				'merges elements when deep nested',
 				'merges elements when deep nested',
-				'<p>x<pchild>fo<selection>o</pchild></p><p><pchild>b</selection>ar</pchild>y</p>',
-				'<p>x<pchild>fo<selection />ar</pchild>y</p>',
+				'<p>x<pchild>fo[o</pchild></p><p><pchild>b]ar</pchild>y</p>',
+				'<p>x<pchild>fo[]ar</pchild>y</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			// For code coverage reasons.
 			// For code coverage reasons.
 			test(
 			test(
 				'merges element when selection is in two consecutive nodes even when it is empty',
 				'merges element when selection is in two consecutive nodes even when it is empty',
-				'<p>foo<selection></p><p></selection>bar</p>',
-				'<p>foo<selection />bar</p>',
+				'<p>foo[</p><p>]bar</p>',
+				'<p>foo[]bar</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			// If you disagree with this case please read the notes before this section.
 			// If you disagree with this case please read the notes before this section.
 			test(
 			test(
 				'merges elements when left end deep nested',
 				'merges elements when left end deep nested',
-				'<p>x<pchild>fo<selection>o</pchild></p><p>b</selection>ary</p>',
-				'<p>x<pchild>fo<selection /></pchild>ary</p>',
+				'<p>x<pchild>fo[o</pchild></p><p>b]ary</p>',
+				'<p>x<pchild>fo[]</pchild>ary</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			// If you disagree with this case please read the notes before this section.
 			// If you disagree with this case please read the notes before this section.
 			test(
 			test(
 				'merges elements when right end deep nested',
 				'merges elements when right end deep nested',
-				'<p>xfo<selection>o</p><p><pchild>b</selection>ar</pchild>y<img></img></p>',
-				'<p>xfo<selection /><pchild>ar</pchild>y<img></img></p>',
+				'<p>xfo[o</p><p><pchild>b]ar</pchild>y<img></img></p>',
+				'<p>xfo[]<pchild>ar</pchild>y<img></img></p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			test(
 			test(
 				'merges elements when more content in the right branch',
 				'merges elements when more content in the right branch',
-				'<p>xfo<selection>o</p><p>b</selection>a<pchild>r</pchild>y</p>',
-				'<p>xfo<selection />a<pchild>r</pchild>y</p>',
+				'<p>xfo[o</p><p>b]a<pchild>r</pchild>y</p>',
+				'<p>xfo[]a<pchild>r</pchild>y</p>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 
 
 			test(
 			test(
 				'leaves just one element when all selected',
 				'leaves just one element when all selected',
-				'<h1><selection>x</h1><p>foo</p><p>y</selection></p>',
-				'<h1><selection /></h1>',
+				'<h1>[x</h1><p>foo</p><p>y]</p>',
+				'<h1>[]</h1>',
 				{ merge: true }
 				{ merge: true }
 			);
 			);
 		} );
 		} );

+ 104 - 93
packages/ckeditor5-engine/tests/model/composer/modifyselection.js

@@ -16,6 +16,9 @@ describe( 'Delete utils', () => {
 	beforeEach( () => {
 	beforeEach( () => {
 		document = new Document();
 		document = new Document();
 		document.schema.registerItem( 'p', '$block' );
 		document.schema.registerItem( 'p', '$block' );
+		document.schema.registerItem( 'x', '$block' );
+		document.schema.registerItem( 'img', '$inline' );
+		document.schema.allow( { name: '$text', inside: '$root' } );
 		document.createRoot();
 		document.createRoot();
 	} );
 	} );
 
 
@@ -24,149 +27,149 @@ describe( 'Delete utils', () => {
 			describe( 'within element', () => {
 			describe( 'within element', () => {
 				test(
 				test(
 					'does nothing on empty content',
 					'does nothing on empty content',
-					'<selection />',
-					'<selection />'
+					'[]',
+					'[]'
 				);
 				);
 
 
 				test(
 				test(
 					'does nothing on empty content (with empty element)',
 					'does nothing on empty content (with empty element)',
-					'<p><selection /></p>',
-					'<p><selection /></p>'
+					'<p>[]</p>',
+					'<p>[]</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'does nothing on empty content (backward)',
 					'does nothing on empty content (backward)',
-					'<selection />',
-					'<selection />',
+					'[]',
+					'[]',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'does nothing on root boundary',
 					'does nothing on root boundary',
-					'<p>foo<selection /></p>',
-					'<p>foo<selection /></p>'
+					'<p>foo[]</p>',
+					'<p>foo[]</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'does nothing on root boundary (backward)',
 					'does nothing on root boundary (backward)',
-					'<p><selection />foo</p>',
-					'<p><selection />foo</p>',
+					'<p>[]foo</p>',
+					'<p>[]foo</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'extends one character forward',
 					'extends one character forward',
-					'<p>f<selection />oo</p>',
-					'<p>f<selection>o</selection>o</p>'
+					'<p>f[]oo</p>',
+					'<p>f[o]o</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends one character backward',
 					'extends one character backward',
-					'<p>fo<selection />o</p>',
-					'<p>f<selection backward>o</selection>o</p>',
+					'<p>fo[]o</p>',
+					'<p>f<selection backward>o]o</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'extends one character forward (non-collapsed)',
 					'extends one character forward (non-collapsed)',
-					'<p>f<selection>o</selection>obar</p>',
-					'<p>f<selection>oo</selection>bar</p>'
+					'<p>f[o]obar</p>',
+					'<p>f[oo]bar</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends one character backward (non-collapsed)',
 					'extends one character backward (non-collapsed)',
-					'<p>foob<selection backward>a</selection>r</p>',
-					'<p>foo<selection backward>ba</selection>r</p>',
+					'<p>foob<selection backward>a]r</p>',
+					'<p>foo<selection backward>ba]r</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'extends to element boundary',
 					'extends to element boundary',
-					'<p>fo<selection />o</p>',
-					'<p>fo<selection>o</selection></p>'
+					'<p>fo[]o</p>',
+					'<p>fo[o]</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends to element boundary (backward)',
 					'extends to element boundary (backward)',
-					'<p>f<selection />oo</p>',
-					'<p><selection backward>f</selection>oo</p>',
+					'<p>f[]oo</p>',
+					'<p><selection backward>f]oo</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'shrinks forward selection (to collapsed)',
 					'shrinks forward selection (to collapsed)',
-					'<p>foo<selection>b</selection>ar</p>',
-					'<p>foo<selection />bar</p>',
+					'<p>foo[b]ar</p>',
+					'<p>foo[]bar</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'shrinks backward selection (to collapsed)',
 					'shrinks backward selection (to collapsed)',
-					'<p>foo<selection backward>b</selection>ar</p>',
-					'<p>foob<selection />ar</p>'
+					'<p>foo<selection backward>b]ar</p>',
+					'<p>foob[]ar</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends one element forward',
 					'extends one element forward',
-					'<p>f<selection /><img></img>oo</p>',
-					'<p>f<selection><img></img></selection>oo</p>'
+					'<p>f[]<img></img>oo</p>',
+					'<p>f[<img></img>]oo</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends one non-empty element forward',
 					'extends one non-empty element forward',
-					'<p>f<selection /><img>x</img>oo</p>',
-					'<p>f<selection><img>x</img></selection>oo</p>'
+					'<p>f[]<img>x</img>oo</p>',
+					'<p>f[<img>x</img>]oo</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends one element backward',
 					'extends one element backward',
-					'<p>fo<img></img><selection />o</p>',
-					'<p>fo<selection backward><img></img></selection>o</p>',
+					'<p>fo<img></img>[]o</p>',
+					'<p>fo<selection backward><img></img>]o</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'unicode support - combining mark forward',
 					'unicode support - combining mark forward',
-					'<p>foo<selection />b̂ar</p>',
-					'<p>foo<selection>b̂</selection>ar</p>'
+					'<p>foo[]b̂ar</p>',
+					'<p>foo[b̂]ar</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'unicode support - combining mark backward',
 					'unicode support - combining mark backward',
-					'<p>foob̂<selection />ar</p>',
-					'<p>foo<selection backward>b̂</selection>ar</p>',
+					'<p>foob̂[]ar</p>',
+					'<p>foo<selection backward>b̂]ar</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'unicode support - combining mark multiple',
 					'unicode support - combining mark multiple',
-					'<p>fo<selection />o̻̐ͩbar</p>',
-					'<p>fo<selection>o̻̐ͩ</selection>bar</p>'
+					'<p>fo[]o̻̐ͩbar</p>',
+					'<p>fo[o̻̐ͩ]bar</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'unicode support - combining mark multiple backward',
 					'unicode support - combining mark multiple backward',
-					'<p>foo̻̐ͩ<selection />bar</p>',
-					'<p>fo<selection backward>o̻̐ͩ</selection>bar</p>',
+					'<p>foo̻̐ͩ[]bar</p>',
+					'<p>fo<selection backward>o̻̐ͩ]bar</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'unicode support - combining mark to the end',
 					'unicode support - combining mark to the end',
-					'<p>fo<selection />o̻̐ͩ</p>',
-					'<p>fo<selection>o̻̐ͩ</selection></p>'
+					'<p>fo[]o̻̐ͩ</p>',
+					'<p>fo[o̻̐ͩ]</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'unicode support - surrogate pairs forward',
 					'unicode support - surrogate pairs forward',
-					'<p><selection />\uD83D\uDCA9</p>',
-					'<p><selection>\uD83D\uDCA9</selection></p>'
+					'<p>[]\uD83D\uDCA9</p>',
+					'<p>[\uD83D\uDCA9]</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'unicode support - surrogate pairs backward',
 					'unicode support - surrogate pairs backward',
-					'<p>\uD83D\uDCA9<selection /></p>',
-					'<p><selection backward>\uD83D\uDCA9</selection></p>',
+					'<p>\uD83D\uDCA9[]</p>',
+					'<p><selection backward>\uD83D\uDCA9]</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 			} );
 			} );
@@ -174,91 +177,98 @@ describe( 'Delete utils', () => {
 			describe( 'beyond element', () => {
 			describe( 'beyond element', () => {
 				test(
 				test(
 					'extends over boundary of empty elements',
 					'extends over boundary of empty elements',
-					'<p><selection /></p><p></p><p></p>',
-					'<p><selection></p><p></selection></p><p></p>'
+					'<p>[]</p><p></p><p></p>',
+					'<p>[</p><p>]</p><p></p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends over boundary of empty elements (backward)',
 					'extends over boundary of empty elements (backward)',
-					'<p></p><p></p><p><selection /></p>',
-					'<p></p><p><selection backward></p><p></selection></p>',
+					'<p></p><p></p><p>[]</p>',
+					'<p></p><p><selection backward></p><p>]</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'extends over boundary of non-empty elements',
 					'extends over boundary of non-empty elements',
-					'<p>a<selection /></p><p>bcd</p>',
-					'<p>a<selection></p><p></selection>bcd</p>'
+					'<p>a[]</p><p>bcd</p>',
+					'<p>a[</p><p>]bcd</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends over boundary of non-empty elements (backward)',
 					'extends over boundary of non-empty elements (backward)',
-					'<p>a</p><p><selection />bcd</p>',
-					'<p>a<selection backward></p><p></selection>bcd</p>',
+					'<p>a</p><p>[]bcd</p>',
+					'<p>a<selection backward></p><p>]bcd</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'extends over character after boundary',
 					'extends over character after boundary',
-					'<p>a<selection></p><p></selection>bcd</p>',
-					'<p>a<selection></p><p>b</selection>cd</p>'
+					'<p>a[</p><p>]bcd</p>',
+					'<p>a[</p><p>b]cd</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends over character after boundary (backward)',
 					'extends over character after boundary (backward)',
-					'<p>abc<selection backward></p><p></selection>d</p>',
-					'<p>ab<selection backward>c</p><p></selection>d</p>',
+					'<p>abc<selection backward></p><p>]d</p>',
+					'<p>ab<selection backward>c</p><p>]d</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'extends over boundary when next element has nested elements',
 					'extends over boundary when next element has nested elements',
-					'<p>a<selection /></p><p><x>bcd</x></p>',
-					'<p>a<selection></p><p></selection><x>bcd</x></p>'
+					'<p>a[]</p><p><x>bcd</x></p>',
+					'<p>a[</p><p>]<x>bcd</x></p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends over element when next element has nested elements',
 					'extends over element when next element has nested elements',
-					'<p>a<selection></p><p></selection><x>bcd</x>ef</p>',
-					'<p>a<selection></p><p><x>bcd</x></selection>ef</p>'
+					'<p>a[</p><p>]<x>bcd</x>ef</p>',
+					'<p>a[</p><p><x>bcd</x>]ef</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'extends over element when next node is a text',
 					'extends over element when next node is a text',
-					'<p>a<selection /></p>bc',
-					'<p>a<selection></p></selection>bc'
+					'<p>a[]</p>bc',
+					'<p>a[</p>]bc'
 				);
 				);
 
 
 				test(
 				test(
 					'extends over element when next node is a text (backward)',
 					'extends over element when next node is a text (backward)',
-					'ab<p><selection />c</p>',
-					'ab<selection backward><p></selection>c</p>',
+					'ab<p>[]c</p>',
+					'ab<selection backward><p>]c</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'shrinks over boundary of empty elements',
 					'shrinks over boundary of empty elements',
-					'<p><selection backward></p><p></selection></p>',
-					'<p></p><p><selection /></p>'
+					'<p><selection backward></p><p>]</p>',
+					'<p></p><p>[]</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'shrinks over boundary of empty elements (backward)',
 					'shrinks over boundary of empty elements (backward)',
-					'<p><selection></p><p></selection></p>',
-					'<p><selection /></p><p></p>',
+					'<p>[</p><p>]</p>',
+					'<p>[]</p><p></p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 
 
 				test(
 				test(
 					'shrinks over boundary of non-empty elements',
 					'shrinks over boundary of non-empty elements',
-					'<p>a<selection backward></p><p></selection>b</p>',
-					'<p>a</p><p><selection />b</p>'
+					'<p>a<selection backward></p><p>]b</p>',
+					'<p>a</p><p>[]b</p>'
 				);
 				);
 
 
 				test(
 				test(
 					'shrinks over boundary of non-empty elements (backward)',
 					'shrinks over boundary of non-empty elements (backward)',
-					'<p>a<selection></p><p></selection>b</p>',
-					'<p>a<selection /></p><p>b</p>',
+					'<p>a[</p><p>]b</p>',
+					'<p>a[]</p><p>b</p>',
+					{ direction: 'backward' }
+				);
+
+				test(
+					'updates selection attributes',
+					'<p><$text bold="true">foo</$text>[b]</p>',
+					'<p><$text bold="true>"foo</$text><selection bold=true />b</p>',
 					{ direction: 'backward' }
 					{ direction: 'backward' }
 				);
 				);
 			} );
 			} );
@@ -267,70 +277,70 @@ describe( 'Delete utils', () => {
 		describe( 'unit=codePoint', () => {
 		describe( 'unit=codePoint', () => {
 			test(
 			test(
 				'does nothing on empty content',
 				'does nothing on empty content',
-				'<selection />',
-				'<selection />',
+				'[]',
+				'[]',
 				{ unit: 'codePoint' }
 				{ unit: 'codePoint' }
 			);
 			);
 
 
 			test(
 			test(
 				'does nothing on empty content (with empty element)',
 				'does nothing on empty content (with empty element)',
-				'<p><selection /></p>',
-				'<p><selection /></p>',
+				'<p>[]</p>',
+				'<p>[]</p>',
 				{ unit: 'codePoint' }
 				{ unit: 'codePoint' }
 			);
 			);
 
 
 			test(
 			test(
 				'extends one user-perceived character forward - latin letters',
 				'extends one user-perceived character forward - latin letters',
-				'<p>f<selection />oo</p>',
-				'<p>f<selection>o</selection>o</p>',
+				'<p>f[]oo</p>',
+				'<p>f[o]o</p>',
 				{ unit: 'codePoint' }
 				{ unit: 'codePoint' }
 			);
 			);
 
 
 			test(
 			test(
 				'extends one user-perceived character backward - latin letters',
 				'extends one user-perceived character backward - latin letters',
-				'<p>fo<selection />o</p>',
-				'<p>f<selection backward>o</selection>o</p>',
+				'<p>fo[]o</p>',
+				'<p>f<selection backward>o]o</p>',
 				{ unit: 'codePoint', direction: 'backward' }
 				{ unit: 'codePoint', direction: 'backward' }
 			);
 			);
 
 
 			test(
 			test(
 				'unicode support - combining mark forward',
 				'unicode support - combining mark forward',
-				'<p>foo<selection />b̂ar</p>',
-				'<p>foo<selection>b</selection>̂ar</p>',
+				'<p>foo[]b̂ar</p>',
+				'<p>foo[b]̂ar</p>',
 				{ unit: 'codePoint' }
 				{ unit: 'codePoint' }
 			);
 			);
 
 
 			test(
 			test(
 				'unicode support - combining mark backward',
 				'unicode support - combining mark backward',
-				'<p>foob̂<selection />ar</p>',
-				'<p>foob<selection backward>̂</selection>ar</p>',
+				'<p>foob̂[]ar</p>',
+				'<p>foob<selection backward>̂]ar</p>',
 				{ unit: 'codePoint', direction: 'backward' }
 				{ unit: 'codePoint', direction: 'backward' }
 			);
 			);
 
 
 			test(
 			test(
 				'unicode support - combining mark multiple',
 				'unicode support - combining mark multiple',
-				'<p>fo<selection />o̻̐ͩbar</p>',
-				'<p>fo<selection>o</selection>̻̐ͩbar</p>',
+				'<p>fo[]o̻̐ͩbar</p>',
+				'<p>fo[o]̻̐ͩbar</p>',
 				{ unit: 'codePoint' }
 				{ unit: 'codePoint' }
 			);
 			);
 
 
 			test(
 			test(
 				'unicode support - surrogate pairs forward',
 				'unicode support - surrogate pairs forward',
-				'<p><selection />\uD83D\uDCA9</p>',
-				'<p><selection>\uD83D\uDCA9</selection></p>',
+				'<p>[]\uD83D\uDCA9</p>',
+				'<p>[\uD83D\uDCA9]</p>',
 				{ unit: 'codePoint' }
 				{ unit: 'codePoint' }
 			);
 			);
 
 
 			test(
 			test(
 				'unicode support surrogate pairs backward',
 				'unicode support surrogate pairs backward',
-				'<p>\uD83D\uDCA9<selection /></p>',
-				'<p><selection backward>\uD83D\uDCA9</selection></p>',
+				'<p>\uD83D\uDCA9[]</p>',
+				'<p><selection backward>\uD83D\uDCA9]</p>',
 				{ unit: 'codePoint', direction: 'backward' }
 				{ unit: 'codePoint', direction: 'backward' }
 			);
 			);
 		} );
 		} );
 	} );
 	} );
 
 
-	function test( title, input, output, options ) {
+	function test( title, input, output, options, isBackward ) {
 		it( title, () => {
 		it( title, () => {
 			input = input.normalize();
 			input = input.normalize();
 			output = output.normalize();
 			output = output.normalize();
@@ -344,6 +354,7 @@ describe( 'Delete utils', () => {
 			modifySelection( testSelection, options );
 			modifySelection( testSelection, options );
 
 
 			expect( stringify( document.getRoot(), testSelection ) ).to.equal( output );
 			expect( stringify( document.getRoot(), testSelection ) ).to.equal( output );
+			expect( document.selection.isBackward ).to.equal( !!isBackward );
 		} );
 		} );
 	}
 	}
 } );
 } );

+ 3 - 3
packages/ckeditor5-engine/tests/view/writer/remove.js

@@ -81,9 +81,9 @@ describe( 'writer', () => {
 
 
 		it( 'should support unicode', () => {
 		it( 'should support unicode', () => {
 			test(
 			test(
-				'<container:p>நி{லை<attribute:b:10>க்}கு</attribute:b:10></container:p>',
-				'<container:p>நி[]<attribute:b:10>கு</attribute:b:10></container:p>',
-				'லை<attribute:b:10>க்</attribute:b:10>'
+				'<container:p>நி{லை<attribute:b view-priority="10">க்}கு</attribute:b></container:p>',
+				'<container:p>நி[]<attribute:b view-priority="10">கு</attribute:b></container:p>',
+				'லை<attribute:b view-priority="10">க்</attribute:b>'
 			);
 			);
 		} );
 		} );
 
 

+ 3 - 3
packages/ckeditor5-engine/tests/view/writer/unwrap.js

@@ -123,9 +123,9 @@ describe( 'writer', () => {
 
 
 		it( 'should support unicode', () => {
 		it( 'should support unicode', () => {
 			test(
 			test(
-				'<container:p>[நிலை<attribute:b:1>க்}கு</attribute:b:1>',
-				'<attribute:b:1></attribute:b:1>',
-				'<container:p>[நிலைக்]<attribute:b:1>கு</attribute:b:1></container:p>'
+				'<container:p>[நிலை<attribute:b view-priority="1">க்}கு</attribute:b></container:p>',
+				'<attribute:b view-priority="1"></attribute:b>',
+				'<container:p>[நிலைக்]<attribute:b view-priority="1">கு</attribute:b></container:p>'
 			);
 			);
 		} );
 		} );
 
 

+ 2 - 2
packages/ckeditor5-engine/tests/view/writer/wrap.js

@@ -110,8 +110,8 @@ describe( 'writer', () => {
 		it( 'should support unicode', () => {
 		it( 'should support unicode', () => {
 			test(
 			test(
 				'<container:p>நி{லை}க்கு</container:p>',
 				'<container:p>நி{லை}க்கு</container:p>',
-				'<attribute:b:1></attribute:b:1>',
-				'<container:p>நி[<attribute:b:1>லை</attribute:b:1>]க்கு</container:p>'
+				'<attribute:b view-priority="1"></attribute:b>',
+				'<container:p>நி[<attribute:b view-priority="1">லை</attribute:b>]க்கு</container:p>'
 			);
 			);
 		} );
 		} );
 
 

+ 2 - 2
packages/ckeditor5-engine/tests/view/writer/wrapposition.js

@@ -57,8 +57,8 @@ describe( 'wrapPosition', () => {
 	it( 'should support unicode', () => {
 	it( 'should support unicode', () => {
 		test(
 		test(
 			'<container:p>நிலை{}க்கு</container:p>',
 			'<container:p>நிலை{}க்கு</container:p>',
-			'<attribute:b:1></attribute:b:1>',
-			'<container:p>நிலை<attribute:b:1>[]</attribute:b:1>க்கு</container:p>'
+			'<attribute:b view-priority="1"></attribute:b>',
+			'<container:p>நிலை<attribute:b view-priority="1">[]</attribute:b>க்கு</container:p>'
 		);
 		);
 	} );
 	} );