import { Client, syncClients, expectClients } from './utils.js'; describe( 'transform', () => { let john, kate; beforeEach( () => { return Promise.all( [ Client.get( 'john' ).then( client => ( john = client ) ), Client.get( 'kate' ).then( client => ( kate = client ) ) ] ); } ); afterEach( () => { return Promise.all( [ john.destroy(), kate.destroy() ] ); } ); describe( 'attribute', () => { describe( 'by attribute', () => { it( 'in text at same path', () => { john.setData( '[Foo] Bar' ); kate.setData( 'Foo [Bar]' ); john.setAttribute( 'bold', true ); kate.setAttribute( 'italic', true ); syncClients(); expectClients( '<$text bold="true">Foo <$text italic="true">Bar' ); } ); it( 'in text at same path, then undo', () => { john.setData( '[Foo] Bar' ); kate.setData( 'Foo [Bar]' ); john.setAttribute( 'bold', true ); kate.setAttribute( 'italic', true ); syncClients(); expectClients( '<$text bold="true">Foo <$text italic="true">Bar' ); john.undo(); syncClients(); expectClients( 'Foo <$text italic="true">Bar' ); } ); it( 'in text in different path', () => { john.setData( 'F[o]oBar' ); kate.setData( 'FooB[a]r' ); john.setAttribute( 'bold', true ); kate.setAttribute( 'italic', true ); syncClients(); expectClients( 'F<$text bold="true">oo' + 'B<$text italic="true">ar' ); } ); it( 'in text with selection inside other client\'s selection #1', () => { john.setData( '[Foo Bar]' ); kate.setData( 'Fo[o B]ar' ); john.setAttribute( 'bold', true ); kate.setAttribute( 'italic', true ); syncClients(); expectClients( '' + '<$text bold="true">Fo<$text bold="true" italic="true">o B<$text bold="true">ar' + '' ); } ); it( 'in text with selection inside other client\'s selection #2', () => { john.setData( 'F[oo Ba]r' ); kate.setData( 'Foo [Bar]' ); john.setAttribute( 'bold', true ); kate.setAttribute( 'italic', true ); syncClients(); expectClients( '' + 'F' + '<$text bold="true">oo ' + '<$text bold="true" italic="true">Ba' + '<$text italic="true">r' + '' ); } ); it( 'in text with selection inside other client\'s selection #3', () => { john.setData( '<$text bold="true">[Foo Bar]' ); kate.setData( '<$text bold="true">Fo[o] Bar' ); john.setAttribute( 'italic', true ); kate.setAttribute( 'underline', true ); syncClients(); expectClients( '' + '<$text bold="true" italic="true">Fo' + '<$text bold="true" italic="true" underline="true">o' + '<$text bold="true" italic="true"> Bar' + '' ); } ); it( 'in text at same position', () => { john.setData( '[Foo Bar]' ); kate.setData( '[Foo Bar]' ); john.setAttribute( 'bold', true ); kate.setAttribute( 'italic', true ); syncClients(); expectClients( '<$text bold="true" italic="true">Foo Bar' ); } ); it( 'in element at same position', () => { john.setData( '[Foo Bar]' ); kate.setData( '[Foo Bar]' ); john.setAttribute( 'bold', true ); kate.setAttribute( 'italic', true ); syncClients(); expectClients( 'Foo Bar' ); } ); it( 'in collapsed selection', () => { john.setData( 'F[]oo Bar' ); kate.setData( 'F[]oo Bar' ); john.setAttribute( 'bold', true ); kate.setAttribute( 'italic', true ); syncClients(); expectClients( 'Foo Bar' ); } ); it( 'in same range, then change attribute', () => { john.setData( '[Foo Bar]' ); kate.setData( '[Foo Bar]' ); john.setAttribute( 'attr', 'foo' ); kate.setAttribute( 'attr', 'bar' ); syncClients(); expectClients( '<$text attr="foo">Foo Bar' ); kate.setAttribute( 'attr', 'bar' ); syncClients(); expectClients( '<$text attr="bar">Foo Bar' ); } ); } ); describe( 'by insert', () => { it( 'text in different path', () => { john.setData( '[Foo]Bar' ); kate.setData( 'Foo[]Bar' ); john.setAttribute( 'bold', true ); kate.type( 'Abc' ); syncClients(); expectClients( '<$text bold="true">FooAbcBar' ); } ); it( 'text in different path #2', () => { john.setData( '[Foo]Bar' ); kate.setData( 'Foo[]Bar' ); john.setAttribute( 'bold', true ); kate.type( 'Abc' ); syncClients(); expectClients( 'FooAbcBar' ); } ); it( 'text at same path', () => { john.setData( '[F]oo' ); kate.setData( 'Foo[]' ); john.setAttribute( 'bold', true ); kate.type( 'Abc' ); syncClients(); expectClients( '<$text bold="true">FooAbc' ); } ); it( 'text inside other client\'s selection', () => { john.setData( '[Foo]' ); kate.setData( 'Fo[]o' ); john.setAttribute( 'bold', true ); kate.type( 'Abc' ); syncClients(); expectClients( '<$text bold="true">FoAbco' ); } ); it( 'element between changed elements', () => { john.setData( '[FooBar]' ); kate.setData( 'Foo[]Bar' ); john.setAttribute( 'bold', true ); kate.insert( 'Abc' ); syncClients(); expectClients( 'Foo' + 'Abc' + 'Bar' ); } ); it( 'element in different path', () => { john.setData( '[Foo]Bar' ); kate.setData( 'FooBar[]' ); john.setAttribute( 'bold', true ); kate.insert( 'Abc' ); syncClients(); expectClients( '<$text bold="true">Foo' + 'Bar' + 'Abc' ); } ); it( 'element in different path #2', () => { john.setData( '[Foo]Bar' ); kate.setData( 'FooBar[]' ); john.setAttribute( 'bold', true ); kate.insert( 'Abc' ); syncClients(); expectClients( 'Foo' + 'Bar' + 'Abc' ); } ); } ); describe( 'by move', () => { it( 'text in different path', () => { john.setData( '[Foo]Bar' ); kate.setData( 'FooB[ar]' ); john.setAttribute( 'bold', true ); kate.move( [ 1, 0 ] ); syncClients(); expectClients( '<$text bold="true">Foo' + 'arB' ); } ); it( 'text in different path #2', () => { john.setData( '[Foo]Bar' ); kate.setData( 'FooB[ar]' ); john.setAttribute( 'bold', true ); kate.move( [ 1, 0 ] ); syncClients(); expectClients( 'Foo' + 'arB' ); } ); it( 'text in same path', () => { john.setData( '[Foo] Bar' ); kate.setData( 'Foo B[ar]' ); john.setAttribute( 'bold', true ); kate.move( [ 0, 4 ] ); syncClients(); expectClients( '<$text bold="true">Foo arB' ); } ); it( 'text to other client\'s selection', () => { john.setData( '[Foo] Bar' ); kate.setData( 'Foo [Bar]' ); john.setAttribute( 'bold', true ); kate.move( [ 0, 1 ] ); syncClients(); expectClients( '<$text bold="true">FBar<$text bold="true">oo ' ); } ); it( 'text from other client\'s selection', () => { john.setData( '[Foo] Bar' ); kate.setData( 'F[oo] Bar' ); john.setAttribute( 'bold', true ); kate.move( [ 0, 7 ] ); syncClients(); expectClients( '<$text bold="true">F Bar<$text bold="true">oo' ); } ); it( 'text from other client\'s selection #2', () => { john.setData( '[Foo Bar]' ); kate.setData( 'F[oo] Bar' ); john.setAttribute( 'bold', true ); kate.move( [ 0, 7 ] ); syncClients(); expectClients( 'F Baroo' ); } ); } ); describe( 'by wrap', () => { it( 'element into blockQuote in different path', () => { john.setData( '[Foo]Bar' ); kate.setData( 'Foo[Bar]' ); john.setAttribute( 'bold', true ); kate.wrap( 'blockQuote' ); syncClients(); expectClients( '<$text bold="true">Foo' + '
Bar
' ); } ); it( 'element into blockQuote in different path #2', () => { john.setData( '[Foo]Bar' ); kate.setData( 'Foo[Bar]' ); john.setAttribute( 'bold', true ); kate.wrap( 'blockQuote' ); syncClients(); expectClients( 'Foo' + '
Bar
' ); } ); it( 'element into blockQuote in same path #1', () => { john.setData( '[Foo]' ); kate.setData( '[Foo]' ); john.setAttribute( 'bold', true ); kate.wrap( 'blockQuote' ); syncClients(); expectClients( '
' + '<$text bold="true">Foo' + '
' ); } ); it( 'element into blockQuote in same path #2', () => { john.setData( '[Foo]' ); kate.setData( '[Foo]' ); john.setAttribute( 'bold', true ); kate.wrap( 'blockQuote' ); syncClients(); expectClients( '
' + 'Foo' + '
' ); } ); } ); describe( 'by unwrap', () => { it( 'element in different path', () => { john.setData( '[Foo]
Bar
' ); kate.setData( 'Foo
[Bar]
' ); john.setAttribute( 'bold', true ); kate.unwrap(); syncClients(); expectClients( '<$text bold="true">Foo' + 'Bar' ); } ); it( 'element in different path #2', () => { john.setData( '[Foo]
Bar
' ); kate.setData( 'Foo
[Bar]
' ); john.setAttribute( 'bold', true ); kate.unwrap(); syncClients(); expectClients( 'Foo' + 'Bar' ); } ); it( 'element in same path #1', () => { john.setData( '
[Foo]
' ); kate.setData( '
[Foo]
' ); john.setAttribute( 'bold', true ); kate.unwrap(); syncClients(); expectClients( '<$text bold="true">Foo' ); } ); it( 'element in same path #2', () => { john.setData( '
[Foo]
' ); kate.setData( '
[Foo]
' ); john.setAttribute( 'bold', true ); kate.unwrap(); syncClients(); expectClients( 'Foo' ); } ); } ); describe( 'by split', () => { it( 'text in different path', () => { john.setData( '[Foo]Bar' ); kate.setData( 'FooBa[]r' ); john.setAttribute( 'bold', true ); kate.split(); syncClients(); expectClients( '<$text bold="true">Foo' + 'Ba' + 'r' ); } ); it( 'text in different path #2', () => { john.setData( '[Foo]Bar' ); kate.setData( 'FooBa[]r' ); john.setAttribute( 'bold', true ); kate.split(); syncClients(); expectClients( 'Foo' + 'Ba' + 'r' ); } ); it( 'text in same path #1', () => { john.setData( '[Foo] Bar' ); kate.setData( 'Foo B[]ar' ); john.setAttribute( 'bold', true ); kate.split(); syncClients(); expectClients( '<$text bold="true">Foo B' + 'ar' ); } ); it( 'text in other user\'s selection', () => { john.setData( '[Foo]' ); kate.setData( 'Fo[]o' ); john.setAttribute( 'bold', true ); kate.split(); syncClients(); expect( '<$text bold="true">Fo' + '<$text bold="true">o' ); } ); it( 'text in other user\'s selection #2', () => { john.setData( '[Foo Bar]' ); kate.setData( 'Foo B[]ar' ); john.setAttribute( 'bold', true ); kate.split(); syncClients(); expectClients( 'Foo B' + 'ar' ); } ); it( 'text while changing attribute', () => { john.setData( '<$text attr="foo">Foo B[ar]' ); kate.setData( '<$text attr="foo">Foo B[]ar' ); john.setAttribute( 'attr', 'bar' ); kate.split(); syncClients(); expectClients( '<$text attr="foo">Foo B' + '<$text attr="bar">ar' ); } ); } ); describe( 'by remove', () => { it( 'text in different path', () => { john.setData( '[Foo]Bar' ); kate.setData( 'Foo[Bar]' ); john.setAttribute( 'bold', true ); kate.remove(); syncClients(); expectClients( '<$text bold="true">Foo' + '' ); } ); it( 'text in same path', () => { john.setData( '[Fo]o' ); kate.setData( 'Fo[o]' ); john.setAttribute( 'bold', true ); kate.remove(); syncClients(); expectClients( '<$text bold="true">Fo' ); } ); it( 'text in other user\'s selection', () => { john.setData( '[Foo]' ); kate.setData( 'F[oo]' ); john.setAttribute( 'bold', true ); kate.remove(); syncClients(); expectClients( '<$text bold="true">F' ); } ); } ); describe( 'by remove attribute', () => { it( 'from element in different path', () => { john.setData( '[Foo]Bar' ); kate.setData( 'Foo[Bar]' ); john.setAttribute( 'bold', true ); kate.removeAttribute( 'bold' ); syncClients(); expectClients( '<$text bold="true">Foo' + 'Bar' ); } ); it( 'from text in different path', () => { john.setData( '[Foo]<$text bold="true">Bar' ); kate.setData( 'Foo<$text bold="true">[Bar]' ); john.setAttribute( 'bold', true ); kate.removeAttribute( 'bold' ); syncClients(); expectClients( '<$text bold="true">Foo' + 'Bar' ); } ); it( 'from text in same path', () => { john.setData( '[Fo]<$text bold="true">o' ); kate.setData( 'Fo<$text bold="true">[o]' ); john.setAttribute( 'bold', true ); kate.removeAttribute( 'bold' ); syncClients(); expectClients( '<$text bold="true">Foo' ); } ); it( 'from element in same path', () => { john.setData( '[Fo]o' ); kate.setData( '[Foo]' ); john.setAttribute( 'italic', true ); kate.removeAttribute( 'bold' ); syncClients(); expectClients( '<$text italic="true">Foo' ); } ); it( 'from text with 2 attributes in same path', () => { john.setData( '[Fo]<$text bold="true" italic="true">o' ); kate.setData( 'Fo<$text bold="true" italic="true">[o]' ); john.setAttribute( 'bold', true ); kate.removeAttribute( 'bold' ); syncClients(); expectClients( '<$text bold="true">Fo<$text italic="true">o' ); } ); it( 'from text in other user\'s selection', () => { john.setData( '<$text bold="true">[Foo]' ); kate.setData( '<$text bold="true">[Foo]' ); john.setAttribute( 'italic', true ); kate.removeAttribute( 'bold' ); syncClients(); expectClients( '<$text italic="true">Foo' ); } ); } ); describe( 'by marker', () => { it( 'in text at same path', () => { john.setData( '[Foo] Bar' ); kate.setData( 'Foo [Bar]' ); john.setAttribute( 'bold', true ); kate.setMarker( 'm1' ); syncClients(); expectClients( '<$text bold="true">Foo Bar' ); } ); it( 'in text at same path #2', () => { john.setData( '[Foo Bar]' ); kate.setData( 'Foo [Bar]' ); john.setAttribute( 'bold', true ); kate.setMarker( 'm1' ); syncClients(); expectClients( '' + 'Foo Bar' + '' ); } ); it( 'in text in different path', () => { john.setData( 'F[o]oBar' ); kate.setData( 'FooB[a]r' ); john.setAttribute( 'bold', true ); kate.setMarker( 'm1' ); syncClients(); expectClients( 'F<$text bold="true">oo' + 'Bar' ); } ); it( 'in text with selection inside other client\'s selection #1', () => { john.setData( '[Foo Bar]' ); kate.setData( 'Fo[o B]ar' ); john.setAttribute( 'bold', true ); kate.setMarker( 'm1' ); syncClients(); expectClients( '' + '<$text bold="true">Fo' + '<$text bold="true">o B<$text bold="true">ar' + '' ); } ); it( 'in text with selection inside other client\'s selection #2', () => { john.setData( 'F[oo Ba]r' ); kate.setData( 'Foo [Bar]' ); john.setAttribute( 'bold', true ); kate.setMarker( 'm1' ); syncClients(); expectClients( '' + 'F<$text bold="true">oo <$text bold="true">Bar' + '' ); } ); it( 'in text at same position', () => { john.setData( '[Foo Bar]' ); kate.setData( '[Foo Bar]' ); john.setAttribute( 'bold', true ); kate.setMarker( 'm1' ); syncClients(); expectClients( '<$text bold="true">Foo Bar' ); } ); } ); describe( 'by merge', () => { it( 'element into paragraph #1', () => { john.setData( 'FooB[ar]' ); kate.setData( 'Foo[]Bar' ); john.setAttribute( 'bold', 'true' ); kate.merge(); syncClients(); expectClients( 'FooB<$text bold="true">ar' ); } ); it( 'element into paragraph #2, then undo', () => { john.setData( 'Foo[Bar]' ); kate.setData( 'Foo[]Bar' ); john.setAttribute( 'bold', 'true' ); kate.merge(); syncClients(); expectClients( 'FooBar' ); kate.undo(); syncClients(); expectClients( 'FooBar' ); } ); } ); } ); describe( 'by rename', () => { it( 'element in different path', () => { john.setData( 'Foo[Ba]r' ); kate.setData( 'F[]ooBar' ); john.setAttribute( 'bold', true ); kate.rename( 'heading1' ); syncClients(); expectClients( 'Foo' + '<$text bold="true">Bar' ); } ); it( 'element in same path', () => { john.setData( '[Foo Bar]' ); kate.setData( '[]Foo Bar' ); john.setAttribute( 'bold', true ); kate.rename( 'heading1' ); syncClients(); expectClients( '<$text bold="true">Foo Bar' ); } ); it( 'element in user\'s selection', () => { john.setData( '[Foo]' ); kate.setData( '[Foo]' ); john.setAttribute( 'bold', true ); kate.rename( 'heading1' ); syncClients(); expectClients( '<$text bold="true">Foo' ); } ); it( 'element in user\'s selection, then undo', () => { john.setData( '[Foo]' ); kate.setData( '[Foo]' ); john.setAttribute( 'bold', true ); kate.rename( 'heading1' ); syncClients(); john.undo(); kate.undo(); syncClients(); expectClients( 'Foo' ); } ); } ); } );