|
|
@@ -0,0 +1,2369 @@
|
|
|
+/**
|
|
|
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
|
|
|
+ * For licensing, see LICENSE.md.
|
|
|
+ */
|
|
|
+
|
|
|
+/* jshint expr: true */
|
|
|
+
|
|
|
+/* bender-tags: document */
|
|
|
+/* global describe, before, beforeEach, it, expect */
|
|
|
+
|
|
|
+'use strict';
|
|
|
+
|
|
|
+const modules = bender.amd.require(
|
|
|
+ 'document/rootelement',
|
|
|
+ 'document/node',
|
|
|
+ 'document/position',
|
|
|
+ 'document/range',
|
|
|
+ 'document/attribute',
|
|
|
+ 'document/operation/transform',
|
|
|
+ 'document/operation/insertoperation',
|
|
|
+ 'document/operation/changeoperation',
|
|
|
+ 'document/operation/moveoperation',
|
|
|
+ 'document/operation/nooperation'
|
|
|
+);
|
|
|
+
|
|
|
+describe( 'transform', () => {
|
|
|
+ let RootElement, Node, Position, Range, Attribute, InsertOperation, ChangeOperation, MoveOperation, NoOperation;
|
|
|
+ let transform;
|
|
|
+
|
|
|
+ before( () => {
|
|
|
+ RootElement = modules[ 'document/rootelement' ];
|
|
|
+ Node = modules[ 'document/node' ];
|
|
|
+ Position = modules[ 'document/position' ];
|
|
|
+ Range = modules[ 'document/range' ];
|
|
|
+ Attribute = modules[ 'document/attribute' ];
|
|
|
+ InsertOperation = modules[ 'document/operation/insertoperation' ];
|
|
|
+ ChangeOperation = modules[ 'document/operation/changeoperation' ];
|
|
|
+ MoveOperation = modules[ 'document/operation/moveoperation' ];
|
|
|
+ NoOperation = modules[ 'document/operation/nooperation' ];
|
|
|
+
|
|
|
+ transform = modules[ 'document/operation/transform' ];
|
|
|
+ } );
|
|
|
+
|
|
|
+ let root, op, nodeA, nodeB, expected, baseVersion;
|
|
|
+
|
|
|
+ beforeEach( () => {
|
|
|
+ root = new RootElement( null );
|
|
|
+
|
|
|
+ nodeA = new Node();
|
|
|
+ nodeB = new Node();
|
|
|
+
|
|
|
+ baseVersion = 0;
|
|
|
+ } );
|
|
|
+
|
|
|
+ function expectOperation( op, params ) {
|
|
|
+ for ( let i in params ) {
|
|
|
+ if ( params.hasOwnProperty( i ) ) {
|
|
|
+ if ( i == 'type' ) {
|
|
|
+ expect( op ).to.be.instanceof( params[ i ] );
|
|
|
+ }
|
|
|
+ else if ( params[ i ] instanceof Array ) {
|
|
|
+ expect( op[ i ].length ).to.equal( params[ i ].length );
|
|
|
+
|
|
|
+ for ( let j = 0; j < params[ i ].length; j++ ) {
|
|
|
+ expect( op[ i ][ j ] ).to.equal( params[ i ][ j ] );
|
|
|
+ }
|
|
|
+ } else if ( params[ i ] instanceof Position || params[ i ] instanceof Range ) {
|
|
|
+ expect( op[ i ].isEqual( params[ i ] ) ).to.be.true;
|
|
|
+ } else {
|
|
|
+ expect( op[ i ] ).to.equal( params[ i ] );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ describe( 'InsertOperation', () => {
|
|
|
+ let nodeC, nodeD, position;
|
|
|
+
|
|
|
+ beforeEach( () => {
|
|
|
+ nodeC = new Node();
|
|
|
+ nodeD = new Node();
|
|
|
+
|
|
|
+ position = new Position( [ 0, 2, 1 ], root );
|
|
|
+
|
|
|
+ op = new InsertOperation( position, [ nodeA, nodeB ], baseVersion );
|
|
|
+
|
|
|
+ expected = {
|
|
|
+ type: InsertOperation,
|
|
|
+ position: position.clone(),
|
|
|
+ baseVersion: baseVersion + 1
|
|
|
+ };
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by InsertOperation', () => {
|
|
|
+ it( 'target at different position: no position update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 1, 3, 2 ], root ),
|
|
|
+ [ nodeC, nodeD ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target at offset before: increment offset', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 0, 2, 0 ], root ),
|
|
|
+ [ nodeC, nodeD ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ expected.position.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target at same offset and is important: increment offset', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 0, 2, 1 ], root ),
|
|
|
+ [ nodeC, nodeD ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ expected.position.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target at same offset and is less important: no position update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 0, 2, 1 ], root ),
|
|
|
+ [ nodeC, nodeD ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target at offset after: no position update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 0, 2, 2 ], root ),
|
|
|
+ [ nodeC, nodeD ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target before node from path: increment index on path', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 0, 1 ], root ),
|
|
|
+ [ nodeC, nodeD ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ expected.position.path[ 1 ] += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target after node from path: no position update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 0, 6 ], root ),
|
|
|
+ [ nodeC, nodeD ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by ChangeOperation', () => {
|
|
|
+ it( 'no position update', () => {
|
|
|
+ let rangeStart = position.clone();
|
|
|
+ let rangeEnd = position.clone();
|
|
|
+ rangeEnd.offset += 2;
|
|
|
+
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( rangeStart, rangeEnd ),
|
|
|
+ null,
|
|
|
+ new Attribute( 'foo', 'bar' ),
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by MoveOperation', () => {
|
|
|
+ it( 'range and target are different than insert position: no position update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 3, 2 ], root ),
|
|
|
+ new Position( [ 2, 1 ], root ),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range offset is before insert position offset: decrement offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 2, 0 ], root ),
|
|
|
+ new Position( [ 1, 1 ], root ),
|
|
|
+ 1
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ expected.position.offset--;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range offset is after insert position offset: no position update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 2, 4 ], root ),
|
|
|
+ new Position( [ 1, 1 ], root ),
|
|
|
+ 1
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset before insert position offset: increment offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 1 ], root ),
|
|
|
+ new Position( [ 0, 2, 0 ], root ),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ expected.position.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset after insert position offset: no position update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 1 ], root ),
|
|
|
+ new Position( [ 0, 2, 4 ], root ),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset same as insert position offset and is important: increment offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 1 ], root ),
|
|
|
+ new Position( [ 0, 2, 1 ], root ),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ expected.position.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset same as insert position offset and is less important: no position update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 1 ], root ),
|
|
|
+ new Position( [ 0, 2, 1 ], root ),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is before node from insert position path: decrement index on path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 0 ], root ),
|
|
|
+ new Position( [ 1, 0 ], root ),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ expected.position.path[ 1 ] -= 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is after node from insert position path: no position update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 4 ], root ),
|
|
|
+ new Position( [ 1, 0 ], root ),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target before node from insert position path: increment index on path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 0 ], root ),
|
|
|
+ new Position( [ 0, 0 ], root ),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ expected.position.path[ 1 ] += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target after node from insert position path: no position update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 0 ], root ),
|
|
|
+ new Position( [ 0, 4 ], root ),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range has node that contains insert position: update position', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 1 ], root ),
|
|
|
+ new Position( [ 1, 1 ], root ),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ expected.position.path = [ 1, 2, 1 ];
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range contains insert position (on same level): set position offset to range start', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 2, 0 ], root ),
|
|
|
+ new Position( [ 1, 0 ], root ),
|
|
|
+ 3
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ expected.position.offset = 0;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by NoOperation', () => {
|
|
|
+ it( 'no operation update', () => {
|
|
|
+ let transformBy = new NoOperation( baseVersion );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'ChangeOperation', () => {
|
|
|
+ let start, end, range, oldAttr, newAttr, anotherOldAttr, anotherNewAttr;
|
|
|
+
|
|
|
+ beforeEach( () => {
|
|
|
+ oldAttr = new Attribute( 'foo', 'abc' );
|
|
|
+ newAttr = new Attribute( 'foo', 'bar' );
|
|
|
+
|
|
|
+ anotherOldAttr = new Attribute( oldAttr.key, 'another' );
|
|
|
+ anotherNewAttr = new Attribute( oldAttr.key, 'anothernew' );
|
|
|
+
|
|
|
+ expected = {
|
|
|
+ type: ChangeOperation,
|
|
|
+ oldAttr: oldAttr,
|
|
|
+ newAttr: newAttr,
|
|
|
+ baseVersion: baseVersion + 1
|
|
|
+ };
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'with multi-level range', () => {
|
|
|
+ beforeEach( () => {
|
|
|
+ start = new Position( [ 1, 2 ], root );
|
|
|
+ end = new Position( [ 2, 2, 4 ], root );
|
|
|
+
|
|
|
+ range = new Range( start, end );
|
|
|
+
|
|
|
+ op = new ChangeOperation( range, oldAttr, newAttr, baseVersion );
|
|
|
+
|
|
|
+ expected.range = new Range( start.clone(), end.clone() );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by InsertOperation', () => {
|
|
|
+ it( 'target at different position: no operation update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 3, 3, 2 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target at offset before: increment offset', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 1, 1 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target at same offset: increment offset', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 1, 2 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target at offset after: no operation update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 3, 2 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target before node from path: increment index on path', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 0 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.path[ 0 ] += 2;
|
|
|
+ expected.range.end.path[ 0 ] += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target after node from path: no position change', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 2, 6 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target inside change range: split into two operations', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 1, 3, 1 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 1, 3, 3 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start = op.range.start;
|
|
|
+ expected.range.end.path = [ 1, 3, 1 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by ChangeOperation', () => {
|
|
|
+ it( 'attributes have different key: no operation update', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ range.clone(),
|
|
|
+ new Attribute( 'abc', true ),
|
|
|
+ new Attribute( 'abc', false ),
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'attributes set same value: no operation update', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ range.clone(),
|
|
|
+ oldAttr,
|
|
|
+ newAttr,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'both operations removes attribute: no operation update', () => {
|
|
|
+ op.newAttr = null;
|
|
|
+
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 1, 1, 4 ], root ), new Position( [ 3 ], root ) ),
|
|
|
+ anotherOldAttr,
|
|
|
+ null,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expected.newAttr = null;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'that is less important and', () => {
|
|
|
+ it( 'range does not intersect original range: no operation update', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 3, 0 ], root ), new Position( [ 4 ], root ) ),
|
|
|
+ anotherOldAttr,
|
|
|
+ null,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range contains original range: update oldAttr', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 1, 1, 4 ], root ), new Position( [ 3 ], root ) ),
|
|
|
+ anotherOldAttr,
|
|
|
+ null,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expected.oldAttr = anotherOldAttr;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ // [ original range < ] range transformed by >
|
|
|
+ it( 'range intersects on left: split into two operations, update oldAttr', () => {
|
|
|
+ // Get more test cases and better code coverage
|
|
|
+ op.newAttr = null;
|
|
|
+
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 1, 4, 2 ], root ), new Position( [ 3 ], root ) ),
|
|
|
+ anotherOldAttr,
|
|
|
+ // Get more test cases and better code coverage
|
|
|
+ anotherNewAttr,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.newAttr = null;
|
|
|
+
|
|
|
+ expected.range.end.path = [ 1, 4, 2 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 1, 4, 2 ];
|
|
|
+ expected.range.end = op.range.end;
|
|
|
+ expected.oldAttr = anotherOldAttr;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ // [ range transformed by < ] original range >
|
|
|
+ it( 'range intersects on right: split into two operations, update oldAttr', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 1 ], root ), new Position( [ 2, 1 ], root ) ),
|
|
|
+ anotherOldAttr,
|
|
|
+ null,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 2, 1 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start = op.range.start;
|
|
|
+ expected.range.end.path = [ 2, 1 ];
|
|
|
+ expected.oldAttr = anotherOldAttr;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is inside original range: split into three operations, update oldAttr', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 1, 4, 1 ], root ), new Position( [ 2, 1 ], root ) ),
|
|
|
+ anotherOldAttr,
|
|
|
+ null,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 3 );
|
|
|
+
|
|
|
+ expected.range.end.path = [ 1, 4, 1 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 2, 1 ];
|
|
|
+ expected.range.end = op.range.end;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 1, 4, 1 ];
|
|
|
+ expected.range.end.path = [ 2, 1 ];
|
|
|
+ expected.oldAttr = anotherOldAttr;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 2 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'that is more important and', () => {
|
|
|
+ it( 'range does not intersect original range: no operation update', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 3, 0 ], root ), new Position( [ 4 ], root ) ),
|
|
|
+ oldAttr,
|
|
|
+ null,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range contains original range: convert to NoOperation', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 1, 1, 4 ], root ), new Position( [ 3 ], root ) ),
|
|
|
+ oldAttr,
|
|
|
+ null,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], {
|
|
|
+ type: NoOperation,
|
|
|
+ baseVersion: baseVersion + 1
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ // [ original range < ] range transformed by >
|
|
|
+ it( 'range intersects on left: shrink original range', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 1, 4, 2 ], root ), new Position( [ 3 ], root ) ),
|
|
|
+ oldAttr,
|
|
|
+ null,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.end.path = [ 1, 4, 2 ];
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ // [ range transformed by < ] original range >
|
|
|
+ it( 'range intersects on right: shrink original range', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 1 ], root ), new Position( [ 2, 1 ], root ) ),
|
|
|
+ oldAttr,
|
|
|
+ null,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 2, 1 ];
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is inside original range: split into two operations', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( new Position( [ 1, 4, 1 ], root ), new Position( [ 2, 1 ], root ) ),
|
|
|
+ oldAttr,
|
|
|
+ null,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.range.end.path = [ 1, 4, 1 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 2, 1 ];
|
|
|
+ expected.range.end = op.range.end;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by MoveOperation', () => {
|
|
|
+ it( 'range and target are different than change range: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 1, 2 ], root ),
|
|
|
+ new Position( [ 3, 4 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range offset is before change range start offset: decrement offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 0 ], root ),
|
|
|
+ new Position( [ 3, 4, 1 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.offset -= 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset is before change range start offset: increment offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3, 4, 1 ], root ),
|
|
|
+ new Position( [ 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is before node from path to change range: decrement index on path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0 ], root ),
|
|
|
+ new Position( [ 2, 4, 1 ], root ),
|
|
|
+ 1,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.path[ 0 ]--;
|
|
|
+ expected.range.end.path[ 0 ]--;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is after node from path to change range: no position change', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3 ], root ),
|
|
|
+ new Position( [ 0, 1 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target before node from path to change range: increment index on path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3, 4, 1 ], root ),
|
|
|
+ new Position( [ 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.path[ 1 ] += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target after node from path to change range: no position change', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3, 4, 1 ], root ),
|
|
|
+ new Position( [ 3 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on left with change range: split into two operations', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 1 ], root ),
|
|
|
+ new Position( [ 4 ], root ),
|
|
|
+ 3,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.range.end.path = [ 2, 1 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 4 ];
|
|
|
+ expected.range.end.path = [ 5, 4 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on right with change range: split into two operation', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 1 ], root ),
|
|
|
+ new Position( [ 0, 0 ], root ),
|
|
|
+ 3,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 1, 1 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 0, 1 ];
|
|
|
+ expected.range.end.path = [ 0, 3 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range contains change range: update change range', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1 ], root ),
|
|
|
+ new Position( [ 3, 4, 1 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 1, 4, 1, 2 ];
|
|
|
+ expected.range.end.path = [ 1, 4, 2, 2, 4 ];
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is inside change range: split into two operations', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 4 ], root ),
|
|
|
+ new Position( [ 3, 2 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 3, 2 ];
|
|
|
+ expected.range.end.path = [ 3, 4 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target inside change range: split into two operations', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3, 4, 1 ], root ),
|
|
|
+ new Position( [ 1, 4 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 1, 6 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start = op.range.start;
|
|
|
+ expected.range.end.path = [ 1, 4 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects change range and target inside change range: split into three operations', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 1 ], root ),
|
|
|
+ new Position( [ 2 ], root ),
|
|
|
+ 3,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 3 );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 5 ];
|
|
|
+ expected.range.end.path = [ 5, 2, 4 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 1, 1 ];
|
|
|
+ expected.range.end.path = [ 2 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 3 ];
|
|
|
+ expected.range.end.path = [ 5 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 2 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by NoOperation', () => {
|
|
|
+ it( 'no operation update', () => {
|
|
|
+ let transformBy = new NoOperation( baseVersion );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ // Some extra cases for a ChangeOperation that operates on single tree level range.
|
|
|
+ // This means that the change range start and end differs only on offset value.
|
|
|
+ // This test suite also have some modifications to the original operation
|
|
|
+ // to get more test cases covered and better code coverage.
|
|
|
+ describe( 'with single-level range', () => {
|
|
|
+ beforeEach( () => {
|
|
|
+ start = new Position( [ 0, 2, 1 ], root );
|
|
|
+ end = new Position( [ 0, 2, 4 ], root );
|
|
|
+
|
|
|
+ range = new Range( start, end );
|
|
|
+
|
|
|
+ op = new ChangeOperation( range, oldAttr, newAttr, baseVersion );
|
|
|
+
|
|
|
+ expected.range = new Range( start.clone(), end.clone() );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by InsertOperation', () => {
|
|
|
+ it( 'target at offset before: increment offset', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 0, 2, 0 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.offset += 2;
|
|
|
+ expected.range.end.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target at same offset: increment offset', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 0, 2, 1 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.offset += 2;
|
|
|
+ expected.range.end.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by MoveOperation', () => {
|
|
|
+ it( 'range offset is before change range start offset: decrement offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 2, 0 ], root ),
|
|
|
+ new Position( [ 2, 4, 1 ], root ),
|
|
|
+ 1,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.offset--;
|
|
|
+ expected.range.end.offset--;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset is before change range start offset: increment offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 4, 1 ], root ),
|
|
|
+ new Position( [ 0, 2, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.offset += 2;
|
|
|
+ expected.range.end.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on left with change range: split into two operations', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 2, 2 ], root ),
|
|
|
+ new Position( [ 2, 4, 1 ], root ),
|
|
|
+ 4,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.range.end.offset -= 2;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 2, 4, 1 ];
|
|
|
+ expected.range.end.path = [ 2, 4, 3 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on right with change range: split into two operation', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 2, 0 ], root ),
|
|
|
+ new Position( [ 2, 4, 1 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.range.start.offset -= 1;
|
|
|
+ expected.range.end.offset -= 2;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 2, 4, 2 ];
|
|
|
+ expected.range.end.path = [ 2, 4, 3 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range contains change range: update change range', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 1 ], root ),
|
|
|
+ new Position( [ 2, 4, 1 ], root ),
|
|
|
+ 3,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 2, 4, 2, 1 ];
|
|
|
+ expected.range.end.path = [ 2, 4, 2, 4 ];
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is inside change range: split into two operations', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 2, 2 ], root ),
|
|
|
+ new Position( [ 2, 4, 1 ], root ),
|
|
|
+ 1,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.range.end.offset--;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 2, 4, 1 ];
|
|
|
+ expected.range.end.path = [ 2, 4, 2 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is same as change range: update change range', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 2, 1 ], root ),
|
|
|
+ new Position( [ 2, 4, 1 ], root ),
|
|
|
+ 3,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.range.start.path = [ 2, 4, 1 ];
|
|
|
+ expected.range.end.path = [ 2, 4, 4 ];
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target inside change range: split into two operations', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 4, 1 ], root ),
|
|
|
+ new Position( [ 0, 2, 2 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.range.start.offset = 4;
|
|
|
+ expected.range.end.offset = 6;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.offset = op.range.start.offset;
|
|
|
+ expected.range.end.offset = 2;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects change range and target inside change range: split into three operations', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0, 2, 0 ], root ),
|
|
|
+ new Position( [ 0, 2, 3 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 3 );
|
|
|
+
|
|
|
+ expected.range.start.offset = 3;
|
|
|
+ expected.range.end.offset = 4;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.offset = 0;
|
|
|
+ expected.range.end.offset = 1;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+
|
|
|
+ expected.range.start.offset = 2;
|
|
|
+ expected.range.end.offset = 3;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 2 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'MoveOperation', () => {
|
|
|
+ let sourcePosition, targetPosition, rangeEnd, howMany;
|
|
|
+
|
|
|
+ beforeEach( () => {
|
|
|
+ sourcePosition = new Position( [ 2, 2, 4 ], root );
|
|
|
+ targetPosition = new Position( [ 3, 3, 3 ], root );
|
|
|
+ howMany = 2;
|
|
|
+
|
|
|
+ rangeEnd = sourcePosition.clone();
|
|
|
+ rangeEnd.offset += howMany;
|
|
|
+
|
|
|
+ op = new MoveOperation( sourcePosition, targetPosition, howMany, baseVersion );
|
|
|
+
|
|
|
+ expected = {
|
|
|
+ type: MoveOperation,
|
|
|
+ sourcePosition: sourcePosition.clone(),
|
|
|
+ targetPosition: targetPosition.clone(),
|
|
|
+ howMany: howMany,
|
|
|
+ baseVersion: baseVersion + 1
|
|
|
+ };
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by InsertOperation', () => {
|
|
|
+ it( 'target at different position than move range and target: no operation update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 1, 3, 2 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target inside node from move range: no operation update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 2, 2, 3, 1 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target at offset before range offset: increment range offset', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 2, 2, 0 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.sourcePosition.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target at offset after range offset: no operation update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 2, 2, 6 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target before node from range start path: increment index on range start path', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 2, 0 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.sourcePosition.path[ 1 ] += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target after node from range start path: no operation update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 2, 3 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset before move target offset: increment target offset', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 3, 3, 2 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.targetPosition.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset after move target offset: no operation update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 3, 3, 4 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target before node from move target position path: increment index on move target position path', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 3, 0 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.targetPosition.path[ 1 ] += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target after node from move target position path: no operation update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 3, 6 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset same as move target offset and is important: increment target offset', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 3, 3, 3 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.targetPosition.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset same as move target offset and is less important: no operation update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 3, 3, 3 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset same as move target offset and is important: increment target offset', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 3, 3 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.targetPosition.path[ 1 ] += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target inside move range: split into two operations', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 2, 2, 5 ], root ),
|
|
|
+ [ nodeA, nodeB ],
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 7 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 4 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by ChangeOperation', () => {
|
|
|
+ it( 'no operation update', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range( sourcePosition, rangeEnd ),
|
|
|
+ new Attribute( 'abc', true ),
|
|
|
+ new Attribute( 'abc', false ),
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by MoveOperation', () => {
|
|
|
+ it( 'range and target different than transforming range and target: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 1, 2 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 3,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset before transforming range start offset: increment range offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ new Position( [ 2, 2, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.sourcePosition.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset after transforming range start offset: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ new Position( [ 2, 2, 7 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range start offset before transforming range start offset: decrement range offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 0 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.sourcePosition.offset -= 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range start offset after transforming range start offset: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 9 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target before node from transforming range start path: increment index on range start path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ new Position( [ 2, 1 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.sourcePosition.path[ 1 ] += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target after node from transforming range start path: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ new Position( [ 2, 5 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range before node from transforming range start path: decrement index on range start path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 0 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.sourcePosition.path[ 1 ] -= 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range after node from transforming range start path: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 3 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset before transforming target offset: increment target offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ new Position( [ 3, 3, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.targetPosition.offset += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target offset after transforming target offset: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ new Position( [ 3, 3, 5 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range offset before transforming target offset: decrement target offset', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3, 3, 0 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.targetPosition.offset -= 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range offset after transforming target offset: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3, 3, 5 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target before node from transforming target path: increment index on target path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ new Position( [ 3, 1 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.targetPosition.path[ 1 ] += 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target after node from transforming target path: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ new Position( [ 3, 5 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range before node from transforming target path: decrement index on target path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3, 0 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.targetPosition.path[ 1 ] -= 2;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range after node from transforming target path: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3, 5 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target inside transforming move range: split into two operations', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ new Position( [ 2, 2, 5 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.howMany = 1;
|
|
|
+ expected.sourcePosition.offset = 7;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.offset = 4;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target inside a node from transforming range: no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ new Position( [ 2, 2, 5, 1 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range has node that contains transforming range: update range path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 1 ], root ),
|
|
|
+ new Position( [ 4, 2 ], root ),
|
|
|
+ 3,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 4, 3, 4 ];
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range has node that contains transforming target: update target path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3, 2 ], root ),
|
|
|
+ new Position( [ 0, 1 ], root ),
|
|
|
+ 3,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.targetPosition.path = [ 0, 2, 3 ];
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'target inside a node from transforming range and vice versa: reverse transform-by operation', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 3, 2 ], root ),
|
|
|
+ new Position( [ 2, 2, 5, 0 ], root ),
|
|
|
+ 3,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+ let reversed = transformBy.getReversed();
|
|
|
+
|
|
|
+ expected.sourcePosition = reversed.sourcePosition;
|
|
|
+ expected.targetPosition = reversed.targetPosition;
|
|
|
+ expected.howMany = reversed.howMany;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is same as transforming range and is important: convert to NoOperation', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ op.sourcePosition.clone(),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ op.howMany,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], {
|
|
|
+ type: NoOperation,
|
|
|
+ baseVersion: baseVersion + 1
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range is same as transforming range and is less important: update range path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ op.sourcePosition.clone(),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ op.howMany,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 4, 1, 0 ];
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range contains transforming range and is important: convert to NoOperation', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 3 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 4,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], {
|
|
|
+ type: NoOperation,
|
|
|
+ baseVersion: baseVersion + 1
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range contains transforming range and is less important: update range path', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 3 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 4,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 4, 1, 1 ];
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range contains transforming range and target and is important: update range path and target', () => {
|
|
|
+ op.targetPosition.path = [ 2, 2, 7 ];
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 3 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 5,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 4, 1, 1 ];
|
|
|
+ expected.targetPosition.path = [ 4, 1, 4 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range contains transforming range and target and is less important: update range path and target', () => {
|
|
|
+ op.targetPosition.path = [ 2, 2, 7 ];
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 3 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 5,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 4, 1, 1 ];
|
|
|
+ expected.targetPosition.path = [ 4, 1, 4 ];
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on left side of transforming range and is important: shrink range', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 3 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 3 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on left side of transforming range and is less important: split into two operations', () => {
|
|
|
+ // Get more test cases and better code coverage
|
|
|
+ let otherRoot = new RootElement( null );
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 3 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], otherRoot ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 3 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition = new Position( [ 4, 1, 1 ], otherRoot );
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on right side of transforming range and is important: shrink range', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 5 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expected.howMany = 1;
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on right side of transforming range and is less important: split into two operations', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 5 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 4, 1, 0 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = op.sourcePosition.path;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on left side, target inside transforming range and is important: split into two operations', () => {
|
|
|
+ op.howMany = 4;
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 3 ], root ),
|
|
|
+ new Position( [ 2, 2, 6 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 6 ];
|
|
|
+ expected.howMany = 2;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 3 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on left side, target inside transforming range and is less important: split into three operations', () => {
|
|
|
+ op.howMany = 4;
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 3 ], root ),
|
|
|
+ new Position( [ 2, 2, 6 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 3 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 6 ];
|
|
|
+ expected.howMany = 2;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 3 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 5 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 2 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on right side, target inside transforming range and is important: split into two operations', () => {
|
|
|
+ op.howMany = 4;
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 7 ], root ),
|
|
|
+ new Position( [ 2, 2, 5 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 7 ];
|
|
|
+ expected.howMany = 2;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 4 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range intersects on right side, target inside transforming range and is less important: split into three operations', () => {
|
|
|
+ op.howMany = 4;
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 7 ], root ),
|
|
|
+ new Position( [ 2, 2, 5 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 3 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 5 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 7 ];
|
|
|
+ expected.howMany = 2;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 4 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 2 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range inside transforming range and is important: split into two operations', () => {
|
|
|
+ op.howMany = 4;
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 5 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 2 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 5 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 4 ];
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range inside transforming range and is less important: split into three operations', () => {
|
|
|
+ op.howMany = 4;
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 5 ], root ),
|
|
|
+ new Position( [ 4, 1, 0 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 3 );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 5 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 4, 1, 0 ];
|
|
|
+ expected.howMany = 2;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 1 ], expected );
|
|
|
+
|
|
|
+ expected.sourcePosition.path = [ 2, 2, 4 ];
|
|
|
+ expected.howMany = 1;
|
|
|
+ expected.baseVersion++;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 2 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range and target inside transforming range and is important: no operation update', () => {
|
|
|
+ op.howMany = 6;
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 5 ], root ),
|
|
|
+ new Position( [ 2, 2, 9 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+
|
|
|
+ expected.howMany = 6;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'range and target inside transforming range and is less important: no operation update', () => {
|
|
|
+ op.howMany = 6;
|
|
|
+
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 2, 2, 5 ], root ),
|
|
|
+ new Position( [ 2, 2, 9 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy, true );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+
|
|
|
+ expected.howMany = 6;
|
|
|
+
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by NoOperation', () => {
|
|
|
+ it( 'no operation update', () => {
|
|
|
+ let transformBy = new NoOperation( baseVersion );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'NoOperation', () => {
|
|
|
+ beforeEach( () => {
|
|
|
+ op = new NoOperation( baseVersion );
|
|
|
+
|
|
|
+ expected = {
|
|
|
+ type: NoOperation,
|
|
|
+ baseVersion: baseVersion + 1
|
|
|
+ };
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by InsertOperation', () => {
|
|
|
+ it( 'no operation update', () => {
|
|
|
+ let transformBy = new InsertOperation(
|
|
|
+ new Position( [ 0 ], root ),
|
|
|
+ 'a',
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by ChangeOperation', () => {
|
|
|
+ it( 'no operation update', () => {
|
|
|
+ let transformBy = new ChangeOperation(
|
|
|
+ new Range(
|
|
|
+ new Position( [ 0 ], root ),
|
|
|
+ new Position( [ 1 ], root )
|
|
|
+ ),
|
|
|
+ new Attribute( 'foo', 'bar' ),
|
|
|
+ new Attribute( 'foo', 'xyz' ),
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by MoveOperation', () => {
|
|
|
+ it( 'no operation update', () => {
|
|
|
+ let transformBy = new MoveOperation(
|
|
|
+ new Position( [ 0 ], root ),
|
|
|
+ new Position( [ 1 ], root ),
|
|
|
+ 2,
|
|
|
+ baseVersion
|
|
|
+ );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'by NoOperation', () => {
|
|
|
+ it( 'no operation update', () => {
|
|
|
+ let transformBy = new NoOperation( baseVersion );
|
|
|
+
|
|
|
+ let transOp = transform( op, transformBy );
|
|
|
+
|
|
|
+ expect( transOp.length ).to.equal( 1 );
|
|
|
+ expectOperation( transOp[ 0 ], expected );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+} );
|