Răsfoiți Sursa

Merge pull request #1494 from ckeditor/t/1470

Tests: New scenarios in OT tests, which include ranges in blocks.
Szymon Cofalik 7 ani în urmă
părinte
comite
c979795416

+ 691 - 23
packages/ckeditor5-engine/tests/model/operation/transform/attribute.js

@@ -232,6 +232,21 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text between changed nodes', () => {
+				john.setData( '<paragraph>[Foo</paragraph><paragraph>Bar]</paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph><paragraph>B[]ar</paragraph>' );
+
+				john.setAttribute( 'bold', true );
+				kate.type( 'Abc' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph><$text bold="true">Foo</$text></paragraph>' +
+					'<paragraph><$text bold="true">BAbcar</$text></paragraph>'
+				);
+			} );
+
 			it( 'element in different path', () => {
 				john.setData( '<paragraph>[Foo]</paragraph><paragraph>Bar</paragraph>' );
 				kate.setData( '<paragraph>Foo</paragraph><paragraph>Bar</paragraph>[]' );
@@ -264,6 +279,66 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'remove attribute from element in different path', () => {
+				john.setData( '<paragraph>[]Foo</paragraph><paragraph bold="true">Bar</paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
+
+				john.type( 'Abc' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph>AbcFoo</paragraph><paragraph>Bar</paragraph>' );
+			} );
+
+			it( 'remove attribute from text in different path', () => {
+				john.setData( '<paragraph>[]Foo</paragraph><paragraph><$text bold="true">Bar</$text></paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph><paragraph><$text bold="true">[Bar]</$text></paragraph>' );
+
+				john.type( 'Abc' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph>AbcFoo</paragraph><paragraph>Bar</paragraph>' );
+			} );
+
+			it( 'remove attribute from text while typing in client\'s range', () => {
+				john.setData( '<paragraph>F<$text bold="true">o[]o</$text></paragraph>' );
+				kate.setData( '<paragraph>F<$text bold="true">[oo]</$text></paragraph>' );
+
+				john.type( 'Bar' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph>FoBaro</paragraph>' );
+			} );
+
+			it( 'remove attribute from element in same path', () => {
+				john.setData( '<paragraph bold="true">[]Foo</paragraph>' );
+				kate.setData( '[<paragraph bold="true">Foo</paragraph>]' );
+
+				john.type( 'Bar' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph>BarFoo</paragraph>' );
+			} );
+
+			it( 'remove attribute from text with 2 attributes while typing in client\'s range', () => {
+				john.setData( '<paragraph>F<$text bold="true" italic="true">o[]o</$text></paragraph>' );
+				kate.setData( '<paragraph>F<$text bold="true" italic="true">[oo]</$text></paragraph>' );
+
+				john.type( 'Bar' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph>F<$text italic="true">oBaro</$text></paragraph>' );
+			} );
+
 			it( 'multiple typing', () => {
 				john.setData( '<paragraph>[Foo]</paragraph>' );
 				kate.setData( '<paragraph>Fo[]o</paragraph>' );
@@ -373,6 +448,87 @@ describe( 'transform', () => {
 
 				expectClients( '<paragraph bold="true">F Baroo</paragraph>' );
 			} );
+
+			it( 'remove attribute from element in different path', () => {
+				john.setData( '<paragraph>F[oo]</paragraph><paragraph bold="true">Bar</paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
+
+				john.move( [ 1, 0 ] );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>F</paragraph>' +
+					'<paragraph>ooBar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in different path', () => {
+				john.setData( '<paragraph>F[oo]</paragraph><paragraph><$text bold="true">Bar</$text></paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph><paragraph><$text bold="true">[Bar]</$text></paragraph>' );
+
+				john.move( [ 1, 0 ] );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>F</paragraph>' +
+					'<paragraph>ooBar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in same path', () => {
+				john.setData( '<paragraph>[Fo]<$text bold="true">o</$text></paragraph>' );
+				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
+
+				john.move( [ 0, 3 ] );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph>oFo</paragraph>' );
+			} );
+
+			it( 'remove attribute from element in same path', () => {
+				john.setData( '<paragraph bold="true">[Fo]o</paragraph>' );
+				kate.setData( '[<paragraph bold="true">Foo</paragraph>]' );
+
+				john.move( [ 0, 3 ] );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph>oFo</paragraph>' );
+			} );
+
+			it( 'remove attribute from text with 2 attributes in same path', () => {
+				john.setData( '<paragraph>[Fo]<$text bold="true" italic="true">o</$text></paragraph>' );
+				kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph>' );
+
+				john.move( [ 0, 3 ] );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph><$text italic="true">o</$text>Fo</paragraph>' );
+			} );
+
+			it( 'remove attribute from text in other user\'s selection', () => {
+				john.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph><paragraph></paragraph>' );
+				kate.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph><paragraph></paragraph>' );
+
+				john.move( [ 1, 0 ] );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph></paragraph>' +
+					'<paragraph>Foo</paragraph>'
+				);
+			} );
 		} );
 
 		describe( 'by wrap', () => {
@@ -437,6 +593,80 @@ describe( 'transform', () => {
 					'</blockQuote>'
 				);
 			} );
+
+			it( 'remove attribute from element in different path', () => {
+				john.setData( '[<paragraph>Foo</paragraph>]<paragraph bold="true">Bar</paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
+
+				john.wrap( 'blockQuote' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<blockQuote>' +
+						'<paragraph>Foo</paragraph>' +
+					'</blockQuote>' +
+					'<paragraph>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in different path', () => {
+				john.setData( '[<paragraph>Foo</paragraph>]<paragraph><$text bold="true">Bar</$text></paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph><paragraph><$text bold="true">[Bar]</$text></paragraph>' );
+
+				john.wrap( 'blockQuote' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<blockQuote>' +
+						'<paragraph>Foo</paragraph>' +
+					'</blockQuote>' +
+					'<paragraph>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in same path', () => {
+				john.setData( '[<paragraph>Fo<$text bold="true">o</$text></paragraph>]' );
+				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
+
+				john.wrap( 'blockQuote' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
+			} );
+
+			it( 'remove attribute from element in same path', () => {
+				john.setData( '[<paragraph bold="true">Foo</paragraph>]' );
+				kate.setData( '[<paragraph bold="true">Foo</paragraph>]' );
+
+				john.wrap( 'blockQuote' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
+			} );
+
+			it( 'remove attribute from text with 2 attributes in same path', () => {
+				john.setData( '[<paragraph>Fo<$text bold="true" italic="true">o</$text></paragraph>]' );
+				kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph>' );
+
+				john.wrap( 'blockQuote' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<blockQuote>' +
+						'<paragraph>Fo<$text italic="true">o</$text></paragraph>' +
+					'</blockQuote>'
+				);
+			} );
 		} );
 
 		describe( 'by unwrap', () => {
@@ -470,6 +700,21 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text in different path', () => {
+				john.setData( '<paragraph>[Foo]</paragraph><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
+				kate.setData( '<paragraph>Foo</paragraph><blockQuote><paragraph>[]Bar</paragraph></blockQuote>' );
+
+				john.setAttribute( 'bold', true );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients(
+					'<paragraph><$text bold="true">Foo</$text></paragraph>' +
+					'<blockQuote>Bar</blockQuote>'
+				);
+			} );
+
 			it( 'element in same path #1', () => {
 				john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
 				kate.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
@@ -479,9 +724,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><$text bold="true">Foo</$text></paragraph>'
-				);
+				expectClients( '<paragraph><$text bold="true">Foo</$text></paragraph>' );
 			} );
 
 			it( 'element in same path #2', () => {
@@ -495,6 +738,92 @@ describe( 'transform', () => {
 
 				expectClients( '<paragraph bold="true">Foo</paragraph>' );
 			} );
+
+			it( 'text in same path', () => {
+				john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.setAttribute( 'bold', true );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients( '<blockQuote><$text bold="true">Foo</$text></blockQuote>' );
+			} );
+
+			it( 'remove attribute from element in different path', () => {
+				john.setData(
+					'<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' +
+					'<paragraph bold="true">Bar</paragraph>' );
+				kate.setData(
+					'<blockQuote><paragraph>Foo</paragraph></blockQuote>' +
+					'[<paragraph bold="true">Bar</paragraph>]' );
+
+				john.unwrap();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>Foo</paragraph>' +
+					'<paragraph>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in different path', () => {
+				john.setData(
+					'<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' +
+					'<paragraph><$text bold="true">Bar</$text></paragraph>' );
+				kate.setData(
+					'<blockQuote><paragraph>Foo</paragraph></blockQuote>' +
+					'<paragraph><$text bold="true">[Bar]</$text></paragraph>' );
+
+				john.unwrap();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<blockQuote>Foo</blockQuote>' +
+					'<paragraph>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in same path', () => {
+				john.setData( '<blockQuote><paragraph>[]Fo<$text bold="true">o</$text></paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>Fo<$text bold="true">[o]</$text></paragraph></blockQuote>' );
+
+				john.unwrap();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<blockQuote>Foo</blockQuote>' );
+			} );
+
+			it( 'remove attribute from element in same path', () => {
+				john.setData( '<blockQuote><paragraph bold="true">[]Foo</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote>[<paragraph bold="true">Foo</paragraph>]</blockQuote>' );
+
+				john.unwrap();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<blockQuote>Foo</blockQuote>' );
+			} );
+
+			it( 'remove attribute from text with 2 attributes in same path', () => {
+				john.setData( '<blockQuote><paragraph>[]Fo<$text bold="true" italic="true">o</$text></paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph></blockQuote>' );
+
+				john.unwrap();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<blockQuote>Fo<$text italic="true">o</$text></blockQuote>' );
+			} );
 		} );
 
 		describe( 'by split', () => {
@@ -589,6 +918,95 @@ describe( 'transform', () => {
 					'<paragraph><$text attr="bar">ar</$text></paragraph>'
 				);
 			} );
+
+			it( 'remove attribute from element in different path', () => {
+				john.setData( '<paragraph>F[]oo</paragraph><paragraph bold="true">Bar</paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
+
+				john.split();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>F</paragraph>' +
+					'<paragraph>oo</paragraph>' +
+					'<paragraph>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in different path', () => {
+				john.setData( '<paragraph>F[]oo</paragraph><paragraph><$text bold="true">Bar</$text></paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph><paragraph><$text bold="true">[Bar]</$text></paragraph>' );
+
+				john.split();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>F</paragraph>' +
+					'<paragraph>oo</paragraph>' +
+					'<paragraph>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in same path #1', () => {
+				john.setData( '<paragraph>F[]o<$text bold="true">o</$text></paragraph>' );
+				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
+
+				john.split();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>F</paragraph>' +
+					'<paragraph>oo</paragraph>' );
+			} );
+
+			it( 'remove attribute from text in same path #2', () => {
+				john.setData( '<paragraph>F<$text bold="true">o[]o</$text></paragraph>' );
+				kate.setData( '<paragraph>F<$text bold="true">[oo]</$text></paragraph>' );
+
+				john.split();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>Fo</paragraph>' +
+					'<paragraph>o</paragraph>' );
+			} );
+
+			it( 'remove attribute from element in same path', () => {
+				john.setData( '<paragraph bold="true">F[]oo</paragraph>' );
+				kate.setData( '[<paragraph bold="true">Foo</paragraph>]' );
+
+				john.split();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>F</paragraph>' +
+					'<paragraph>oo</paragraph>' );
+			} );
+
+			it( 'remove attribute from text with 2 attributes in same path', () => {
+				john.setData( '<paragraph>F<$text bold="true" italic="true">o[]o</$text></paragraph>' );
+				kate.setData( '<paragraph>F<$text bold="true" italic="true">[oo]</$text></paragraph>' );
+
+				john.split();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>F<$text italic="true">o</$text></paragraph>' +
+					'<paragraph><$text italic="true">o</$text></paragraph>'
+				);
+			} );
 		} );
 
 		describe( 'by remove', () => {
@@ -616,9 +1034,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><$text bold="true">Fo</$text></paragraph>'
-				);
+				expectClients( '<paragraph><$text bold="true">Fo</$text></paragraph>' );
 			} );
 
 			it( 'text in other user\'s selection', () => {
@@ -630,10 +1046,114 @@ describe( 'transform', () => {
 
 				syncClients();
 
+				expectClients( '<paragraph><$text bold="true">F</$text></paragraph>' );
+			} );
+
+			it( 'remove attribute from element in different path', () => {
+				john.setData( '<paragraph>F[oo]</paragraph><paragraph bold="true">Bar</paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
+
+				john.remove();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>F</paragraph>' +
+					'<paragraph>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in different path', () => {
+				john.setData( '<paragraph>F[oo]</paragraph><paragraph><$text bold="true">Bar</$text></paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph><paragraph><$text bold="true">[Bar]</$text></paragraph>' );
+
+				john.remove();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>F</paragraph>' +
+					'<paragraph>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in same path #1', () => {
+				john.setData( '<paragraph>[Fo]<$text bold="true">o</$text></paragraph>' );
+				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
+
+				john.remove();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph>o</paragraph>' );
+			} );
+
+			it( 'remove attribute from text in same path #2', () => {
+				john.setData( '<paragraph>F[o<$text bold="true">o]z</$text>Bar</paragraph>' );
+				kate.setData( '<paragraph>Fo<$text bold="true">[oz]</$text>Bar</paragraph>' );
+
+				john.remove();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
 				expectClients(
-					'<paragraph><$text bold="true">F</$text></paragraph>'
+					'<paragraph>FzBar</paragraph>'
 				);
 			} );
+
+			it( 'remove attribute from element in same path', () => {
+				john.setData( '<paragraph bold="true">[Fo]o</paragraph>' );
+				kate.setData( '[<paragraph bold="true">Foo</paragraph>]' );
+
+				john.remove();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph>o</paragraph>' );
+			} );
+
+			it( 'remove attribute from text with 2 attributes in same path #1', () => {
+				john.setData( '<paragraph>[Fo]<$text bold="true" italic="true">o</$text></paragraph>' );
+				kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph>' );
+
+				john.remove();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph><$text italic="true">o</$text></paragraph>' );
+			} );
+
+			it( 'remove attribute from text with 2 attributes in same path #2', () => {
+				john.setData( '<paragraph>F[o<$text bold="true" italic="true">o]z</$text>Bar</paragraph>' );
+				kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[oz]</$text>Bar</paragraph>' );
+
+				john.remove();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>F<$text italic="true">z</$text>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in other user\'s selection', () => {
+				john.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
+				kate.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
+
+				john.remove();
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph></paragraph>' );
+			} );
 		} );
 
 		describe( 'by remove attribute', () => {
@@ -676,9 +1196,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><$text bold="true">Fo</$text>o</paragraph>'
-				);
+				expectClients( '<paragraph><$text bold="true">Fo</$text>o</paragraph>' );
 			} );
 
 			it( 'from element in same path', () => {
@@ -702,9 +1220,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><$text bold="true">Fo</$text><$text italic="true">o</$text></paragraph>'
-				);
+				expectClients( '<paragraph><$text bold="true">Fo</$text><$text italic="true">o</$text></paragraph>' );
 			} );
 
 			it( 'from text in other user\'s selection', () => {
@@ -716,9 +1232,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><$text italic="true">Foo</$text></paragraph>'
-				);
+				expectClients( '<paragraph><$text italic="true">Foo</$text></paragraph>' );
 			} );
 		} );
 
@@ -810,6 +1324,88 @@ describe( 'transform', () => {
 
 				expectClients( '<paragraph><m1:start></m1:start><$text bold="true">Foo Bar</$text><m1:end></m1:end></paragraph>' );
 			} );
+
+			it( 'remove attribute from element in different path', () => {
+				john.setData( '<paragraph>[Foo]</paragraph><paragraph bold="true">Bar</paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
+
+				john.setMarker( 'm1' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end></paragraph>' +
+					'<paragraph>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in different path', () => {
+				john.setData( '<paragraph>[Foo]</paragraph><paragraph><$text bold="true">Bar</$text></paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph><paragraph><$text bold="true">[Bar]</$text></paragraph>' );
+
+				john.setMarker( 'm1' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end></paragraph>' +
+					'<paragraph>Bar</paragraph>'
+				);
+			} );
+
+			it( 'remove attribute from text in same path', () => {
+				john.setData( '<paragraph>[Fo]<$text bold="true">o</$text></paragraph>' );
+				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
+
+				john.setMarker( 'm1' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph><m1:start></m1:start>Fo<m1:end></m1:end>o</paragraph>' );
+			} );
+
+			it( 'remove attribute from text in same path, then undo', () => {
+				john.setData( '<paragraph>[Fo]<$text bold="true">o</$text></paragraph>' );
+				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
+
+				john.setMarker( 'm1' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				kate.undo();
+
+				syncClients();
+
+				expectClients( '<paragraph><m1:start></m1:start>Fo<m1:end></m1:end><$text bold="true">o</$text></paragraph>' );
+			} );
+
+			it( 'remove attribute from text with 2 attributes in same path', () => {
+				john.setData( '<paragraph>[Fo]<$text bold="true" italic="true">o</$text></paragraph>' );
+				kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph>' );
+
+				john.setMarker( 'm1' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph><m1:start></m1:start>Fo<m1:end></m1:end><$text italic="true">o</$text></paragraph>' );
+			} );
+
+			it( 'remove attribute from text in other user\'s selection', () => {
+				john.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
+				kate.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
+
+				john.setMarker( 'm1' );
+				kate.removeAttribute( 'bold' );
+
+				syncClients();
+
+				expectClients( '<paragraph><m1:start></m1:start>Foo<m1:end></m1:end></paragraph>' );
+			} );
 		} );
 
 		describe( 'by merge', () => {
@@ -868,9 +1464,7 @@ describe( 'transform', () => {
 
 			syncClients();
 
-			expectClients(
-				'<heading1><$text bold="true">Foo Bar</$text></heading1>'
-			);
+			expectClients( '<heading1><$text bold="true">Foo Bar</$text></heading1>' );
 		} );
 
 		it( 'element in user\'s selection', () => {
@@ -882,9 +1476,7 @@ describe( 'transform', () => {
 
 			syncClients();
 
-			expectClients(
-				'<heading1><$text bold="true">Foo</$text></heading1>'
-			);
+			expectClients( '<heading1><$text bold="true">Foo</$text></heading1>' );
 		} );
 
 		it( 'element in user\'s selection, then undo', () => {
@@ -901,9 +1493,85 @@ describe( 'transform', () => {
 
 			syncClients();
 
+			expectClients( '<paragraph>Foo</paragraph>' );
+		} );
+
+		it( 'remove attribute from element in different path', () => {
+			john.setData( '<paragraph>F[]oo</paragraph><paragraph bold="true">Bar</paragraph>' );
+			kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
+
+			john.rename( 'heading1' );
+			kate.removeAttribute( 'bold' );
+
+			syncClients();
+
+			expectClients(
+				'<heading1>Foo</heading1>' +
+				'<paragraph>Bar</paragraph>'
+			);
+		} );
+
+		it( 'remove attribute from text in different path', () => {
+			john.setData( '<paragraph>F[]oo</paragraph><paragraph><$text bold="true">Bar</$text></paragraph>' );
+			kate.setData( '<paragraph>Foo</paragraph><paragraph><$text bold="true">[Bar]</$text></paragraph>' );
+
+			john.rename( 'heading1' );
+			kate.removeAttribute( 'bold' );
+
+			syncClients();
+
 			expectClients(
-				'<paragraph>Foo</paragraph>'
+				'<heading1>Foo</heading1>' +
+				'<paragraph>Bar</paragraph>'
 			);
 		} );
+
+		it( 'remove attribute from text in same path', () => {
+			john.setData( '<paragraph>F[]o<$text bold="true">o</$text></paragraph>' );
+			kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
+
+			john.rename( 'heading1' );
+			kate.removeAttribute( 'bold' );
+
+			syncClients();
+
+			expectClients( '<heading1>Foo</heading1>' );
+		} );
+
+		it( 'remove attribute from element in same path', () => {
+			john.setData( '<paragraph bold="true">F[]oo</paragraph>' );
+			kate.setData( '[<paragraph bold="true">Foo</paragraph>]' );
+
+			john.rename( 'heading1' );
+			kate.removeAttribute( 'bold' );
+
+			syncClients();
+
+			expectClients( '<heading1>Foo</heading1>' );
+		} );
+
+		it( 'remove attribute from text with 2 attributes in same path', () => {
+			john.setData( '<paragraph>F[o]<$text bold="true" italic="true">o</$text></paragraph>' );
+			kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph>' );
+
+			john.rename( 'heading1' );
+			kate.removeAttribute( 'bold' );
+
+			syncClients();
+
+			expectClients( '<heading1>Fo<$text italic="true">o</$text></heading1>' );
+		} );
+
+		it( 'remove attribute from text in other user\'s selection', () => {
+			john.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
+			kate.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
+
+			john.rename( 'heading1' );
+			kate.removeAttribute( 'bold' );
+
+			syncClients();
+
+			expectClients( '<heading1>Foo</heading1>' );
+		} );
 	} );
 } );

+ 50 - 90
packages/ckeditor5-engine/tests/model/operation/transform/insert.js

@@ -175,9 +175,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph></paragraph>'
-				);
+				expectClients( '<paragraph></paragraph>' );
 			} );
 		} );
 
@@ -216,6 +214,21 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text at different paths', () => {
+				john.setData( '<paragraph>[]Foo</paragraph><paragraph>Bar</paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph><paragraph>B[ar]</paragraph>' );
+
+				john.type( 'Abc' );
+				kate.move( [ 1, 0 ] );
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>AbcFoo</paragraph>' +
+					'<paragraph>arB</paragraph>'
+				);
+			} );
+
 			it( 'text at same path', () => {
 				john.setData( '<paragraph>F[]oo Bar</paragraph>' );
 				kate.setData( '<paragraph>Foo B[ar]</paragraph>' );
@@ -254,7 +267,7 @@ describe( 'transform', () => {
 		} );
 
 		describe( 'by wrap', () => {
-			it( 'element in same path', () => {
+			it( 'element in same path #1', () => {
 				john.setData( '<paragraph>Foo Bar</paragraph>[]' );
 				kate.setData( '[<paragraph>Foo Bar</paragraph>]' );
 
@@ -271,7 +284,7 @@ describe( 'transform', () => {
 				);
 			} );
 
-			it( 'element in same path', () => {
+			it( 'element in same path #2', () => {
 				john.setData( '<paragraph>Foo[]</paragraph>' );
 				kate.setData( '[<paragraph>Foo</paragraph>]' );
 
@@ -287,7 +300,7 @@ describe( 'transform', () => {
 				);
 			} );
 
-			it( 'element in different paths', () => {
+			it( 'element in different paths #1', () => {
 				john.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
 				kate.setData( '<paragraph>Foo</paragraph>[<paragraph>Bar</paragraph>]' );
 
@@ -305,7 +318,7 @@ describe( 'transform', () => {
 				);
 			} );
 
-			it( 'element in different paths', () => {
+			it( 'element in different paths #2', () => {
 				john.setData( '<paragraph>Foo</paragraph><paragraph>Bar[]</paragraph>' );
 				kate.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
 
@@ -444,9 +457,19 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>FooAbc</paragraph><paragraph>Bar</paragraph>'
-				);
+				expectClients( '<paragraph>FooAbc</paragraph><paragraph>Bar</paragraph>' );
+			} );
+
+			it( 'text in different path', () => {
+				john.setData( '<paragraph>Foo[]</paragraph><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
+				kate.setData( '<paragraph>Foo</paragraph><blockQuote><paragraph>[]Bar</paragraph></blockQuote>' );
+
+				john.type( 'Abc' );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients( '<paragraph>FooAbc</paragraph><blockQuote>Bar</blockQuote>' );
 			} );
 
 			it( 'element in same path #1', () => {
@@ -458,9 +481,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>Foo Bar</paragraph>'
-				);
+				expectClients( '<paragraph>Foo Bar</paragraph>' );
 			} );
 
 			it( 'element in same path #2', () => {
@@ -478,6 +499,18 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text in same path', () => {
+				john.setData( '<blockQuote><paragraph>Foo[]</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.type( ' Bar' );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients( '<blockQuote>Foo Bar</blockQuote>' );
+			} );
+
 			it( 'element, then insert text and move', () => {
 				john.setData( '<blockQuote>[]<paragraph>Foo</paragraph></blockQuote>' );
 				kate.setData( '<blockQuote>[]<paragraph>Foo</paragraph></blockQuote>' );
@@ -700,9 +733,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>FooAbc</paragraph><paragraph></paragraph>'
-				);
+				expectClients( '<paragraph>FooAbc</paragraph><paragraph></paragraph>' );
 			} );
 
 			it( 'text in same path', () => {
@@ -714,9 +745,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>Bar</paragraph>'
-				);
+				expectClients( '<paragraph>Bar</paragraph>' );
 			} );
 
 			it( 'element in different path', () => {
@@ -859,9 +888,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>Foo Bar</paragraph>'
-				);
+				expectClients( '<paragraph>Foo Bar</paragraph>' );
 			} );
 		} );
 
@@ -906,74 +933,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>BarFo<m1:start></m1:start>o<m1:end></m1:end></paragraph>'
-				);
-			} );
-		} );
-
-		// This should be moved to attribute.js.
-		describe( 'by remove attribute', () => {
-			it( 'from element in different path', () => {
-				john.setData( '<paragraph>[]Foo</paragraph><paragraph bold="true">Bar</paragraph>' );
-				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
-
-				john.type( 'Abc' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients( '<paragraph>AbcFoo</paragraph><paragraph>Bar</paragraph>' );
-			} );
-
-			it( 'from text in different path', () => {
-				john.setData( '<paragraph>[]Foo</paragraph><paragraph><$text bold="true">Bar</$text></paragraph>' );
-				kate.setData( '<paragraph>Foo</paragraph><paragraph><$text bold="true">[Bar]</$text></paragraph>' );
-
-				john.type( 'Abc' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients( '<paragraph>AbcFoo</paragraph><paragraph>Bar</paragraph>' );
-			} );
-
-			it( 'from text in same path', () => {
-				john.setData( '<paragraph>[]Fo<$text bold="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
-
-				john.type( 'Bar' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients( '<paragraph>BarFoo</paragraph>' );
-			} );
-
-			it( 'from element in same path', () => {
-				john.setData( '<paragraph bold="true">[]Foo</paragraph>' );
-				kate.setData( '[<paragraph bold="true">Foo</paragraph>]' );
-
-				john.type( 'Bar' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients( '<paragraph>BarFoo</paragraph>' );
-			} );
-
-			it( 'from text with 2 attributes in same path', () => {
-				john.setData( '<paragraph>[]Fo<$text bold="true" italic="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph>' );
-
-				john.type( 'Bar' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph>BarFo<$text italic="true">o</$text></paragraph>'
-				);
+				expectClients( '<paragraph>BarFo<m1:start></m1:start>o<m1:end></m1:end></paragraph>' );
 			} );
 		} );
 

+ 59 - 133
packages/ckeditor5-engine/tests/model/operation/transform/marker.js

@@ -159,9 +159,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph></paragraph>'
-				);
+				expectClients( '<paragraph></paragraph>' );
 			} );
 
 			it.skip( 'then remove text and undo', () => {
@@ -208,7 +206,9 @@ describe( 'transform', () => {
 				syncClients();
 
 				expectClients(
-					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end> <m2:start></m2:start>Bar<m2:end></m2:end></paragraph>'
+					'<paragraph>' +
+						'<m1:start></m1:start>Foo<m1:end></m1:end> <m2:start></m2:start>Bar<m2:end></m2:end>' +
+					'</paragraph>'
 				);
 
 				john.setSelection( [ 0, 1 ], [ 0, 3 ] );
@@ -219,9 +219,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><m1:start></m1:start>F<m1:end></m1:end> oo</paragraph>'
-				);
+				expectClients( '<paragraph><m1:start></m1:start>F<m1:end></m1:end> oo</paragraph>' );
 			} );
 
 			it( 'then unwrap and merge', () => {
@@ -312,9 +310,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>Foo Bar</paragraph>'
-				);
+				expectClients( '<paragraph>Foo Bar</paragraph>' );
 			} );
 
 			it( 'with the same name', () => {
@@ -355,9 +351,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>o<m1:start></m1:start>Fo<m1:end></m1:end></paragraph>'
-				);
+				expectClients( '<paragraph>o<m1:start></m1:start>Fo<m1:end></m1:end></paragraph>' );
 			} );
 
 			it( 'text from other user\'s range #1', () => {
@@ -371,9 +365,7 @@ describe( 'transform', () => {
 
 				// Actual result for Kate:
 				// <paragraph>Ba<m1:start></m1:start>Foo r<m1:end></m1:end></paragraph>
-				expectClients(
-					'<paragraph><m1:start></m1:start>BaFoo r<m1:end></m1:end></paragraph>'
-				);
+				expectClients( '<paragraph><m1:start></m1:start>BaFoo r<m1:end></m1:end></paragraph>' );
 			} );
 
 			it( 'text from other user\'s range #2', () => {
@@ -434,9 +426,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end> </paragraph>'
-				);
+				expectClients( '<paragraph><m1:start></m1:start>Foo<m1:end></m1:end> </paragraph>' );
 			} );
 
 			it( 'text in other user\'s selection #1', () => {
@@ -448,9 +438,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><m1:start></m1:start>F<m1:end></m1:end></paragraph>'
-				);
+				expectClients( '<paragraph><m1:start></m1:start>F<m1:end></m1:end></paragraph>' );
 			} );
 
 			it( 'text in other user\'s selection #2', () => {
@@ -462,9 +450,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph></paragraph>'
-				);
+				expectClients( '<paragraph></paragraph>' );
 			} );
 		} );
 
@@ -539,6 +525,21 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text in different path', () => {
+				john.setData( '<paragraph>[Foo]</paragraph><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
+				kate.setData( '<paragraph>Foo</paragraph><blockQuote><paragraph>[]Bar</paragraph></blockQuote>' );
+
+				john.setMarker( 'm1' );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients(
+					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end></paragraph>' +
+					'<blockQuote>Bar</blockQuote>'
+				);
+			} );
+
 			it( 'element in same path', () => {
 				john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
 				kate.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
@@ -548,9 +549,19 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end></paragraph>'
-				);
+				expectClients( '<paragraph><m1:start></m1:start>Foo<m1:end></m1:end></paragraph>' );
+			} );
+
+			it( 'text in same path', () => {
+				john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.setMarker( 'm1' );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients( '<blockQuote><m1:start></m1:start>Foo<m1:end></m1:end></blockQuote>' );
 			} );
 
 			it( 'element in same path, then undo', () => {
@@ -566,9 +577,23 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>Foo</paragraph>'
-				);
+				expectClients( '<paragraph>Foo</paragraph>' );
+			} );
+
+			it( 'text in same path, then undo', () => {
+				john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.setMarker( 'm1' );
+				kate.unwrap();
+
+				syncClients();
+
+				john.undo();
+
+				syncClients();
+
+				expectClients( '<blockQuote>Foo</blockQuote>' );
 			} );
 		} );
 
@@ -644,99 +669,6 @@ describe( 'transform', () => {
 			} );
 		} );
 
-		// Should be in attribute.js.
-		describe( 'by remove attribute', () => {
-			it( 'from element in different path', () => {
-				john.setData( '<paragraph>[Foo]</paragraph><paragraph bold="true">Bar</paragraph>' );
-				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
-
-				john.setMarker( 'm1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end></paragraph>' +
-					'<paragraph>Bar</paragraph>'
-				);
-			} );
-
-			it( 'from text in different path', () => {
-				john.setData( '<paragraph>[Foo]</paragraph><paragraph><$text bold="true">Bar</$text></paragraph>' );
-				kate.setData( '<paragraph>Foo</paragraph><paragraph><$text bold="true">[Bar]</$text></paragraph>' );
-
-				john.setMarker( 'm1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end></paragraph>' +
-					'<paragraph>Bar</paragraph>'
-				);
-			} );
-
-			it( 'from text in same path', () => {
-				john.setData( '<paragraph>[Fo]<$text bold="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
-
-				john.setMarker( 'm1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph><m1:start></m1:start>Fo<m1:end></m1:end>o</paragraph>'
-				);
-			} );
-
-			it( 'from text in same path, then undo', () => {
-				john.setData( '<paragraph>[Fo]<$text bold="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
-
-				john.setMarker( 'm1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				kate.undo();
-
-				syncClients();
-
-				expectClients(
-					'<paragraph><m1:start></m1:start>Fo<m1:end></m1:end><$text bold="true">o</$text></paragraph>'
-				);
-			} );
-
-			it( 'from text with 2 attributes in same path', () => {
-				john.setData( '<paragraph>[Fo]<$text bold="true" italic="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph>' );
-
-				john.setMarker( 'm1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph><m1:start></m1:start>Fo<m1:end></m1:end><$text italic="true">o</$text></paragraph>'
-				);
-			} );
-
-			it( 'from text in other user\'s selection', () => {
-				john.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
-				kate.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
-
-				john.setMarker( 'm1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end></paragraph>'
-				);
-			} );
-		} );
-
 		describe( 'by merge', () => {
 			it( 'element into paragraph', () => {
 				john.setData( '<paragraph>[Foo]</paragraph><paragraph> Bar</paragraph>' );
@@ -747,9 +679,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end> Bar</paragraph>'
-				);
+				expectClients( '<paragraph><m1:start></m1:start>Foo<m1:end></m1:end> Bar</paragraph>' );
 			} );
 
 			it( 'elements into paragraph', () => {
@@ -767,9 +697,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><m1:start></m1:start>Foo<m1:end></m1:end> Bar Abc</paragraph>'
-				);
+				expectClients( '<paragraph><m1:start></m1:start>Foo<m1:end></m1:end> Bar Abc</paragraph>' );
 			} );
 
 			it( 'wrapped element into wrapped paragraph', () => {
@@ -849,9 +777,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<heading1>Fo<m1:start></m1:start>o<m1:end></m1:end></heading1>'
-				);
+				expectClients( '<heading1>Fo<m1:start></m1:start>o<m1:end></m1:end></heading1>' );
 			} );
 		} );
 	} );

+ 48 - 113
packages/ckeditor5-engine/tests/model/operation/transform/move.js

@@ -224,21 +224,6 @@ describe( 'transform', () => {
 					'</blockQuote>'
 				);
 			} );
-
-			it( 'intersecting wrap', () => {
-				john.setData( '<paragraph>F[oo]</paragraph><paragraph>Bar</paragraph>' );
-				kate.setData( '<paragraph>[Foo]</paragraph><paragraph>Bar</paragraph>' );
-
-				john.move( [ 1, 0 ] );
-				kate.wrap( 'div' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph><div>F</div></paragraph>' +
-					'<paragraph>ooBar</paragraph>'
-				);
-			} );
 		} );
 
 		describe( 'by unwrap', () => {
@@ -272,6 +257,21 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text in different path', () => {
+				john.setData( '<paragraph>F[oo]</paragraph><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
+				kate.setData( '<paragraph>Foo</paragraph><blockQuote><paragraph>[]Bar</paragraph></blockQuote>' );
+
+				john.move( [ 0, 0 ] );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients(
+					'<paragraph>ooF</paragraph>' +
+					'<blockQuote>Bar</blockQuote>'
+				);
+			} );
+
 			it( 'element in same path', () => {
 				john.setData( '<blockQuote><paragraph>F[oo]</paragraph></blockQuote>' );
 				kate.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
@@ -281,9 +281,19 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>ooF</paragraph>'
-				);
+				expectClients( '<paragraph>ooF</paragraph>' );
+			} );
+
+			it( 'text in same path', () => {
+				john.setData( '<blockQuote><paragraph>F[oo]</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.move( [ 0, 0, 0 ] );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients( '<blockQuote>ooF</blockQuote>' );
 			} );
 		} );
 
@@ -475,9 +485,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>Foo Bar</paragraph>'
-				);
+				expectClients( '<paragraph>Foo Bar</paragraph>' );
 			} );
 		} );
 
@@ -491,9 +499,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>ooFBar</paragraph>'
-				);
+				expectClients( '<paragraph>ooFBar</paragraph>' );
 			} );
 
 			it( 'element into paragraph #2', () => {
@@ -505,9 +511,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>arFooB</paragraph>'
-				);
+				expectClients( '<paragraph>arFooB</paragraph>' );
 			} );
 
 			it( 'element into paragraph #3', () => {
@@ -519,9 +523,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>FooBar</paragraph>'
-				);
+				expectClients( '<paragraph>FooBar</paragraph>' );
 			} );
 
 			it( 'wrapped element into wrapped paragraph #1', () => {
@@ -533,9 +535,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<blockQuote><paragraph>ooFBar</paragraph></blockQuote>'
-				);
+				expectClients( '<blockQuote><paragraph>ooFBar</paragraph></blockQuote>' );
 			} );
 
 			it( 'wrapped element into wrapped paragraph #2', () => {
@@ -547,9 +547,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<blockQuote><paragraph>arFooB</paragraph></blockQuote>'
-				);
+				expectClients( '<blockQuote><paragraph>arFooB</paragraph></blockQuote>' );
 			} );
 
 			it( 'wrapped element into wrapped paragraph #3', () => {
@@ -561,9 +559,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<blockQuote><paragraph>FooBar</paragraph></blockQuote>'
-				);
+				expectClients( '<blockQuote><paragraph>FooBar</paragraph></blockQuote>' );
 			} );
 
 			it( 'moved element', () => {
@@ -594,94 +590,33 @@ describe( 'transform', () => {
 
 				expectClients( '<paragraph>FooBar</paragraph>' );
 			} );
-		} );
-
-		describe( 'by remove attribute', () => {
-			it( 'from element in different path', () => {
-				john.setData( '<paragraph>F[oo]</paragraph><paragraph bold="true">Bar</paragraph>' );
-				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
-
-				john.move( [ 1, 0 ] );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph>F</paragraph>' +
-					'<paragraph>ooBar</paragraph>'
-				);
-			} );
-
-			it( 'from text in different path', () => {
-				john.setData( '<paragraph>F[oo]</paragraph><paragraph>Bar</paragraph>' );
-				kate.setData( '<paragraph>Foo</paragraph><paragraph>[Bar]</paragraph>' );
-
-				john.move( [ 1, 0 ] );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph>F</paragraph>' +
-					'<paragraph>ooBar</paragraph>'
-				);
-			} );
-
-			it( 'from text in same path', () => {
-				john.setData( '<paragraph>[Fo]<$text bold="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
-
-				john.move( [ 0, 3 ] );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph>oFo</paragraph>'
-				);
-			} );
 
-			it( 'from element in same path', () => {
-				john.setData( '<paragraph bold="true">[Fo]o</paragraph>' );
-				kate.setData( '[<paragraph bold="true">Foo</paragraph>]' );
+			it( 'moved text', () => {
+				john.setData( '<paragraph>Foo</paragraph><paragraph>B[ar]</paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
 
-				john.move( [ 0, 3 ] );
-				kate.removeAttribute( 'bold' );
+				john.move( [ 0, 0 ] );
+				kate.merge();
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>oFo</paragraph>'
-				);
+				expectClients( '<paragraph>arFooB</paragraph>' );
 			} );
 
-			it( 'from text with 2 attributes in same path', () => {
-				john.setData( '<paragraph>[Fo]<$text bold="true" italic="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph>' );
+			it( 'moved text, then undo', () => {
+				john.setData( '<paragraph>Foo</paragraph><paragraph>B[ar]</paragraph>' );
+				kate.setData( '<paragraph>Foo</paragraph>[]<paragraph>Bar</paragraph>' );
 
-				john.move( [ 0, 3 ] );
-				kate.removeAttribute( 'bold' );
+				john.move( [ 0, 0 ] );
+				kate.merge();
 
 				syncClients();
 
-				expectClients(
-					'<paragraph><$text italic="true">o</$text>Fo</paragraph>'
-				);
-			} );
-
-			it( 'from text in other user\'s selection', () => {
-				john.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph><paragraph></paragraph>' );
-				kate.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph><paragraph></paragraph>' );
-
-				john.move( [ 1, 0 ] );
-				kate.removeAttribute( 'bold' );
+				john.undo();
 
 				syncClients();
 
-				expectClients(
-					'<paragraph></paragraph>' +
-					'<paragraph>Foo</paragraph>'
-				);
+				expectClients( '<paragraph>FooBar</paragraph>' );
 			} );
 		} );
 

+ 54 - 133
packages/ckeditor5-engine/tests/model/operation/transform/remove.js

@@ -25,9 +25,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>F</paragraph><paragraph>B</paragraph>'
-				);
+				expectClients( '<paragraph>F</paragraph><paragraph>B</paragraph>' );
 			} );
 
 			it( 'text in same path', () => {
@@ -39,9 +37,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>F B</paragraph>'
-				);
+				expectClients( '<paragraph>F B</paragraph>' );
 			} );
 
 			it( 'text in other user\'s selection #1', () => {
@@ -53,9 +49,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph></paragraph>'
-				);
+				expectClients( '<paragraph></paragraph>' );
 			} );
 
 			it( 'text in other user\'s selection #2', () => {
@@ -67,9 +61,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph></paragraph>'
-				);
+				expectClients( '<paragraph></paragraph>' );
 			} );
 
 			it( 'element in different path', () => {
@@ -81,9 +73,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph></paragraph>'
-				);
+				expectClients( '<paragraph></paragraph>' );
 			} );
 
 			it( 'text in other user\'s selection, then undo', () => {
@@ -100,9 +90,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>Foo Bar</paragraph>'
-				);
+				expectClients( '<paragraph>Foo Bar</paragraph>' );
 			} );
 		} );
 
@@ -133,9 +121,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<blockQuote><paragraph></paragraph></blockQuote>'
-				);
+				expectClients( '<blockQuote><paragraph></paragraph></blockQuote>' );
 			} );
 
 			it( 'element while removing', () => {
@@ -207,6 +193,21 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text in different path', () => {
+				john.setData( '<paragraph>[Foo]</paragraph><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
+				kate.setData( '<paragraph>Foo</paragraph><blockQuote><paragraph>[]Bar</paragraph></blockQuote>' );
+
+				john.remove();
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients(
+					'<paragraph></paragraph>' +
+					'<blockQuote>Bar</blockQuote>'
+				);
+			} );
+
 			it( 'element in same path', () => {
 				john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
 				kate.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
@@ -216,9 +217,31 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph></paragraph>'
-				);
+				expectClients( '<paragraph></paragraph>' );
+			} );
+
+			it( 'text in same path', () => {
+				john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.remove();
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients( '<blockQuote></blockQuote>' );
+			} );
+
+			it( 'text in same path, then undo', () => {
+				john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.remove();
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients( '<blockQuote>Foo</blockQuote>' );
 			} );
 
 			it( 'element while removing', () => {
@@ -230,9 +253,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>Foo</paragraph>'
-				);
+				expectClients( '<paragraph>Foo</paragraph>' );
 			} );
 
 			it( 'element while removing, then undo', () => {
@@ -333,97 +354,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph></paragraph>'
-				);
-			} );
-		} );
-
-		describe( 'by remove attribute', () => {
-			it( 'from element in different path', () => {
-				john.setData( '<paragraph>F[oo]</paragraph><paragraph bold="true">Bar</paragraph>' );
-				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
-
-				john.remove();
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph>F</paragraph>' +
-					'<paragraph>Bar</paragraph>'
-				);
-			} );
-
-			it( 'from text in different path', () => {
-				john.setData( '<paragraph>F[oo]</paragraph><paragraph><$text bold="true">Bar</$text></paragraph>' );
-				kate.setData( '<paragraph>Foo</paragraph><paragraph><$text bold="true">[Bar]</$text></paragraph>' );
-
-				john.remove();
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph>F</paragraph>' +
-					'<paragraph>Bar</paragraph>'
-				);
-			} );
-
-			it( 'from text in same path', () => {
-				john.setData( '<paragraph>[Fo]<$text bold="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
-
-				john.remove();
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph>o</paragraph>'
-				);
-			} );
-
-			it( 'from element in same path', () => {
-				john.setData( '<paragraph bold="true">[Fo]o</paragraph>' );
-				kate.setData( '[<paragraph bold="true">Foo</paragraph>]' );
-
-				john.remove();
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph>o</paragraph>'
-				);
-			} );
-
-			it( 'from text with 2 attributes in same path', () => {
-				john.setData( '<paragraph>[Fo]<$text bold="true" italic="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph>' );
-
-				john.remove();
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph><$text italic="true">o</$text></paragraph>'
-				);
-			} );
-
-			it( 'from text in other user\'s selection', () => {
-				john.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
-				kate.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
-
-				john.remove();
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<paragraph></paragraph>'
-				);
+				expectClients( '<paragraph></paragraph>' );
 			} );
 		} );
 
@@ -437,9 +368,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>FBar</paragraph>'
-				);
+				expectClients( '<paragraph>FBar</paragraph>' );
 			} );
 
 			it( 'element into paragraph #2', () => {
@@ -451,9 +380,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>FooB</paragraph>'
-				);
+				expectClients( '<paragraph>FooB</paragraph>' );
 			} );
 
 			it( 'element into paragraph, then undo', () => {
@@ -481,9 +408,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<blockQuote><paragraph>FBar</paragraph></blockQuote>'
-				);
+				expectClients( '<blockQuote><paragraph>FBar</paragraph></blockQuote>' );
 			} );
 
 			it( 'wrapped element into wrapped paragraph #2', () => {
@@ -495,9 +420,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<blockQuote><paragraph>FooB</paragraph></blockQuote>'
-				);
+				expectClients( '<blockQuote><paragraph>FooB</paragraph></blockQuote>' );
 			} );
 
 			it( 'removed element, then undo', () => {
@@ -555,9 +478,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<heading1></heading1>'
-				);
+				expectClients( '<heading1></heading1>' );
 			} );
 		} );
 	} );

+ 37 - 108
packages/ckeditor5-engine/tests/model/operation/transform/rename.js

@@ -15,94 +15,6 @@ describe( 'transform', () => {
 	} );
 
 	describe( 'rename', () => {
-		describe( 'by remove attribute', () => {
-			it( 'from element in different path', () => {
-				john.setData( '<paragraph>F[]oo</paragraph><paragraph bold="true">Bar</paragraph>' );
-				kate.setData( '<paragraph>Foo</paragraph>[<paragraph bold="true">Bar</paragraph>]' );
-
-				john.rename( 'heading1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<heading1>Foo</heading1>' +
-					'<paragraph>Bar</paragraph>'
-				);
-			} );
-
-			it( 'from text in different path', () => {
-				john.setData( '<paragraph>F[]oo</paragraph><paragraph>Bar</paragraph>' );
-				kate.setData( '<paragraph>Foo</paragraph><paragraph>[Bar]</paragraph>' );
-
-				john.rename( 'heading1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<heading1>Foo</heading1>' +
-					'<paragraph>Bar</paragraph>'
-				);
-			} );
-
-			it( 'from text in same path', () => {
-				john.setData( '<paragraph>F[]o<$text bold="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true">[o]</$text></paragraph>' );
-
-				john.rename( 'heading1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<heading1>Foo</heading1>'
-				);
-			} );
-
-			it( 'from element in same path', () => {
-				john.setData( '<paragraph bold="true">F[]oo</paragraph>' );
-				kate.setData( '[<paragraph bold="true">Foo</paragraph>]' );
-
-				john.rename( 'heading1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<heading1>Foo</heading1>'
-				);
-			} );
-
-			it( 'from text with 2 attributes in same path', () => {
-				john.setData( '<paragraph>F[o]<$text bold="true" italic="true">o</$text></paragraph>' );
-				kate.setData( '<paragraph>Fo<$text bold="true" italic="true">[o]</$text></paragraph>' );
-
-				john.rename( 'heading1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<heading1>Fo<$text italic="true">o</$text></heading1>'
-				);
-			} );
-
-			it( 'from text in other user\'s selection', () => {
-				john.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
-				kate.setData( '<paragraph><$text bold="true">[Foo]</$text></paragraph>' );
-
-				john.rename( 'heading1' );
-				kate.removeAttribute( 'bold' );
-
-				syncClients();
-
-				expectClients(
-					'<heading1>Foo</heading1>'
-				);
-			} );
-		} );
-
 		describe( 'by rename', () => {
 			it( 'elements in different paths #1', () => {
 				john.setData( '<paragraph>[]Foo</paragraph><paragraph>Bar</paragraph>' );
@@ -129,15 +41,15 @@ describe( 'transform', () => {
 			} );
 
 			it( 'the same element', () => {
-				john.setData( '<blockQuote>[]<paragraph>Foo Bar</paragraph></blockQuote>' );
-				kate.setData( '<blockQuote>[]<paragraph>Foo Bar</paragraph></blockQuote>' );
+				john.setData( '<blockQuote><paragraph>[]Foo Bar</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo Bar</paragraph></blockQuote>' );
 
-				john.rename( 'blockQuote2' );
-				kate.rename( 'blockQuote3' );
+				john.rename( 'heading1' );
+				kate.rename( 'heading2' );
 
 				syncClients();
 
-				expectClients( '<blockQuote2><paragraph>Foo Bar</paragraph></blockQuote2>' );
+				expectClients( '<blockQuote><heading1>Foo Bar</heading1></blockQuote>' );
 			} );
 		} );
 
@@ -247,6 +159,21 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text in different path', () => {
+				john.setData( '<paragraph>F[]oo</paragraph><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
+				kate.setData( '<paragraph>Foo</paragraph><blockQuote><paragraph>[]Bar</paragraph></blockQuote>' );
+
+				john.rename( 'heading1' );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients(
+					'<heading1>Foo</heading1>' +
+					'<blockQuote>Bar</blockQuote>'
+				);
+			} );
+
 			it( 'element in same path', () => {
 				john.setData( '<blockQuote><paragraph>F[]oo</paragraph></blockQuote>' );
 				kate.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
@@ -256,9 +183,19 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<heading1>Foo</heading1>'
-				);
+				expectClients( '<heading1>Foo</heading1>' );
+			} );
+
+			it( 'text in same path', () => {
+				john.setData( '<blockQuote><paragraph>F[]oo</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.rename( 'heading1' );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients( '<blockQuote>Foo</blockQuote>' );
 			} );
 		} );
 
@@ -272,9 +209,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<heading1>FooBar</heading1>'
-				);
+				expectClients( '<heading1>FooBar</heading1>' );
 			} );
 
 			it( 'element into paragraph #2', () => {
@@ -286,9 +221,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>FooBar</paragraph>'
-				);
+				expectClients( '<paragraph>FooBar</paragraph>' );
 			} );
 
 			it( 'wrapped element into wrapped paragraph #1', () => {
@@ -300,9 +233,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<blockQuote><heading1>FooBar</heading1></blockQuote>'
-				);
+				expectClients( '<blockQuote><heading1>FooBar</heading1></blockQuote>' );
 			} );
 
 			it( 'wrapped element into wrapped paragraph #2', () => {
@@ -314,9 +245,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<blockQuote><paragraph>FooBar</paragraph></blockQuote>'
-				);
+				expectClients( '<blockQuote><paragraph>FooBar</paragraph></blockQuote>' );
 			} );
 		} );
 	} );

+ 33 - 6
packages/ckeditor5-engine/tests/model/operation/transform/split.js

@@ -121,6 +121,21 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text in same path', () => {
+				john.setData( '<blockQuote><paragraph>F[]oo</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.split();
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients(
+					'<blockQuote>F</blockQuote>' +
+					'<blockQuote>oo</blockQuote>'
+				);
+			} );
+
 			it( 'element in same position', () => {
 				john.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
 				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
@@ -145,9 +160,23 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>Foo</paragraph>'
-				);
+				expectClients( '<paragraph>Foo</paragraph>' );
+			} );
+
+			it( 'text in same path, then undo', () => {
+				john.setData( '<blockQuote><paragraph>F[]oo</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.split();
+				kate.unwrap();
+
+				syncClients();
+
+				john.undo();
+
+				syncClients();
+
+				expectClients( '<blockQuote>Foo</blockQuote>' );
 			} );
 
 			it( 'multiple elements', () => {
@@ -228,9 +257,7 @@ describe( 'transform', () => {
 
 				syncClients();
 
-				expectClients(
-					'<paragraph>Foo</paragraph>'
-				);
+				expectClients( '<paragraph>Foo</paragraph>' );
 			} );
 
 			it( 'text in different path', () => {

+ 51 - 1
packages/ckeditor5-engine/tests/model/operation/transform/unwrap.js

@@ -38,6 +38,28 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text in different path', () => {
+				john.setData(
+					'<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' +
+					'<blockQuote><paragraph>Bar</paragraph></blockQuote>'
+				);
+
+				kate.setData(
+					'<blockQuote><paragraph>Foo</paragraph></blockQuote>' +
+					'<blockQuote><paragraph>[]Bar</paragraph></blockQuote>'
+				);
+
+				john.unwrap();
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients(
+					'<blockQuote>Foo</blockQuote>' +
+					'<blockQuote>Bar</blockQuote>'
+				);
+			} );
+
 			it( 'the same element', () => {
 				john.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
 				kate.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
@@ -65,10 +87,38 @@ describe( 'transform', () => {
 				syncClients();
 				expectClients( '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
 			} );
+
+			it( 'the same text', () => {
+				john.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.unwrap();
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients( '<blockQuote>Foo</blockQuote>' );
+			} );
+
+			it( 'the same text, then undo', () => {
+				john.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.unwrap();
+				kate.unwrap();
+
+				syncClients();
+
+				john.undo();
+
+				syncClients();
+
+				expectClients( '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
+			} );
 		} );
 
 		describe( 'by delete', () => {
-			it( 'text from two elements', () => {
+			it( 'text from two elements #1', () => {
 				john.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote><paragraph>Bar</paragraph>' );
 				kate.setData( '<blockQuote><paragraph>Fo[o</paragraph></blockQuote><paragraph>Ba]r</paragraph>' );
 

+ 56 - 1
packages/ckeditor5-engine/tests/model/operation/transform/wrap.js

@@ -201,6 +201,21 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text in different path', () => {
+				john.setData( '<paragraph>[Foo]</paragraph><blockQuote><paragraph>Bar</paragraph></blockQuote>' );
+				kate.setData( '<paragraph>Foo</paragraph><blockQuote><paragraph>[]Bar</paragraph></blockQuote>' );
+
+				john.wrap( 'div' );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients(
+					'<paragraph><div>Foo</div></paragraph>' +
+					'<blockQuote>Bar</blockQuote>'
+				);
+			} );
+
 			it( 'the same element', () => {
 				john.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
 				kate.setData( '<blockQuote>[<paragraph>Foo</paragraph>]</blockQuote>' );
@@ -228,10 +243,38 @@ describe( 'transform', () => {
 
 				expectClients( '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
 			} );
+
+			it( 'the same text', () => {
+				john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.wrap( 'div' );
+				kate.unwrap();
+
+				syncClients();
+
+				expectClients( '<blockQuote><div>Foo</div></blockQuote>' );
+			} );
+
+			it( 'the same text, then undo', () => {
+				john.setData( '<blockQuote><paragraph>[Foo]</paragraph></blockQuote>' );
+				kate.setData( '<blockQuote><paragraph>[]Foo</paragraph></blockQuote>' );
+
+				john.wrap( 'div' );
+				kate.unwrap();
+
+				syncClients();
+
+				kate.undo();
+
+				syncClients();
+
+				expectClients( '<blockQuote><paragraph><div>Foo</div></paragraph></blockQuote>' );
+			} );
 		} );
 
 		describe( 'by delete', () => {
-			it( 'text in two elements', () => {
+			it( 'text in two elements #1', () => {
 				john.setData( '[<paragraph>Foo</paragraph>]<paragraph>Bar</paragraph>' );
 				kate.setData( '<paragraph>Fo[o</paragraph><paragraph>Ba]r</paragraph>' );
 
@@ -247,6 +290,18 @@ describe( 'transform', () => {
 				);
 			} );
 
+			it( 'text in two elements #2', () => {
+				john.setData( '<paragraph>[Foo]</paragraph><paragraph>Bar</paragraph>' );
+				kate.setData( '<paragraph>Fo[o</paragraph><paragraph>Ba]r</paragraph>' );
+
+				john.wrap( 'div' );
+				kate.delete();
+
+				syncClients();
+
+				expectClients( '<paragraph><div>Fo</div>r</paragraph>' );
+			} );
+
 			it( 'delete all wrapped content', () => {
 				john.setData( '[<paragraph>Foo</paragraph><paragraph>Bar</paragraph><paragraph>Abc</paragraph>]' );
 				kate.setData( '<paragraph>[Foo</paragraph><paragraph>Bar</paragraph><paragraph>Ab]c</paragraph>' );