splitdelta.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import transformations from '../../../../src/model/delta/basic-transformations'; // eslint-disable-line no-unused-vars
  6. import deltaTransform from '../../../../src/model/delta/transform';
  7. const transform = deltaTransform.transform;
  8. import Element from '../../../../src/model/element';
  9. import Position from '../../../../src/model/position';
  10. import Range from '../../../../src/model/range';
  11. import Delta from '../../../../src/model/delta/delta';
  12. import SplitDelta from '../../../../src/model/delta/splitdelta';
  13. import AttributeDelta from '../../../../src/model/delta/attributedelta';
  14. import RenameDelta from '../../../../src/model/delta/renamedelta';
  15. import InsertOperation from '../../../../src/model/operation/insertoperation';
  16. import AttributeOperation from '../../../../src/model/operation/attributeoperation';
  17. import ReinsertOperation from '../../../../src/model/operation/reinsertoperation';
  18. import MoveOperation from '../../../../src/model/operation/moveoperation';
  19. import NoOperation from '../../../../src/model/operation/nooperation';
  20. import RenameOperation from '../../../../src/model/operation/renameoperation';
  21. import { getNodesAndText } from '../../../../tests/model/_utils/utils';
  22. import {
  23. applyDelta,
  24. expectDelta,
  25. getFilledDocument,
  26. getSplitDelta,
  27. getWrapDelta,
  28. getUnwrapDelta,
  29. getRemoveDelta
  30. } from '../../../../tests/model/delta/transform/_utils/utils';
  31. describe( 'transform', () => {
  32. let doc, root, gy, baseVersion, context;
  33. beforeEach( () => {
  34. doc = getFilledDocument();
  35. root = doc.getRoot();
  36. gy = doc.graveyard;
  37. baseVersion = doc.version;
  38. context = { isStrong: false };
  39. } );
  40. describe( 'SplitDelta by', () => {
  41. let splitDelta, splitPosition;
  42. beforeEach( () => {
  43. splitPosition = new Position( root, [ 3, 3, 3, 3 ] );
  44. splitDelta = getSplitDelta( splitPosition, new Element( 'p' ), 9, baseVersion );
  45. } );
  46. describe( 'SplitDelta', () => {
  47. it( 'split in same parent and offset', () => {
  48. const splitDeltaB = getSplitDelta( splitPosition, new Element( 'p' ), 9, baseVersion );
  49. const transformed = transform( splitDelta, splitDeltaB, context );
  50. baseVersion = splitDeltaB.operations.length;
  51. expect( transformed.length ).to.equal( 1 );
  52. expectDelta( transformed[ 0 ], {
  53. type: Delta,
  54. operations: [
  55. {
  56. type: NoOperation,
  57. baseVersion
  58. }
  59. ]
  60. } );
  61. // Test if deltas do what they should after applying transformed delta.
  62. applyDelta( splitDeltaB, doc );
  63. applyDelta( transformed[ 0 ], doc );
  64. const nodesAndText = getNodesAndText( Range.createFromPositionAndShift( new Position( root, [ 3, 3, 0 ] ), 5 ) );
  65. // Incoming split delta is discarded. Only one new element is created after applying both split deltas.
  66. // There are no empty P elements.
  67. expect( nodesAndText ).to.equal( 'XXXXXabcdXPabcPPfoobarxyzP' );
  68. } );
  69. it( 'split in same parent, incoming delta splits closer', () => {
  70. const splitDeltaB = getSplitDelta( new Position( root, [ 3, 3, 3, 5 ] ), new Element( 'p' ), 7, baseVersion );
  71. const transformed = transform( splitDelta, splitDeltaB, context );
  72. baseVersion = splitDeltaB.operations.length;
  73. expect( transformed.length ).to.equal( 1 );
  74. expectDelta( transformed[ 0 ], {
  75. type: SplitDelta,
  76. operations: [
  77. {
  78. type: InsertOperation,
  79. position: new Position( root, [ 3, 3, 4 ] ),
  80. baseVersion
  81. },
  82. {
  83. type: MoveOperation,
  84. sourcePosition: new Position( root, [ 3, 3, 3, 3 ] ),
  85. howMany: 2,
  86. targetPosition: new Position( root, [ 3, 3, 4, 0 ] ),
  87. baseVersion: baseVersion + 1
  88. }
  89. ]
  90. } );
  91. // Test if deltas do what they should after applying transformed delta.
  92. applyDelta( splitDeltaB, doc );
  93. applyDelta( transformed[ 0 ], doc );
  94. const nodesAndText = getNodesAndText( Range.createFromPositionAndShift( new Position( root, [ 3, 3, 0 ] ), 6 ) );
  95. // P element is correctly split, there are three P elements, letters in P elements are in correct order.
  96. expect( nodesAndText ).to.equal( 'XXXXXabcdXPabcPPfoPPobarxyzP' );
  97. } );
  98. it( 'split in same parent, incoming delta splits closer, split deltas have reinsert operations', () => {
  99. let reOp = new ReinsertOperation(
  100. new Position( gy, [ 1 ] ),
  101. 1,
  102. Position.createFromPosition( splitDelta.operations[ 0 ].position ),
  103. splitDelta.operations[ 0 ].baseVersion
  104. );
  105. splitDelta.operations[ 0 ] = reOp;
  106. const splitDeltaB = getSplitDelta( new Position( root, [ 3, 3, 3, 5 ] ), new Element( 'p' ), 7, baseVersion );
  107. reOp = new ReinsertOperation(
  108. new Position( gy, [ 0 ] ),
  109. 1,
  110. Position.createFromPosition( splitDeltaB.operations[ 0 ].position ),
  111. splitDeltaB.operations[ 0 ].baseVersion
  112. );
  113. splitDeltaB.operations[ 0 ] = reOp;
  114. const transformed = transform( splitDelta, splitDeltaB, context );
  115. baseVersion = splitDeltaB.operations.length;
  116. expect( transformed.length ).to.equal( 1 );
  117. expectDelta( transformed[ 0 ], {
  118. type: SplitDelta,
  119. operations: [
  120. {
  121. type: ReinsertOperation,
  122. sourcePosition: new Position( gy, [ 0 ] ),
  123. howMany: 1,
  124. targetPosition: new Position( root, [ 3, 3, 4 ] ),
  125. baseVersion
  126. },
  127. {
  128. type: MoveOperation,
  129. sourcePosition: new Position( root, [ 3, 3, 3, 3 ] ),
  130. howMany: 2,
  131. targetPosition: new Position( root, [ 3, 3, 4, 0 ] ),
  132. baseVersion: baseVersion + 1
  133. }
  134. ]
  135. } );
  136. } );
  137. it( 'split in same parent, incoming delta splits further', () => {
  138. const splitDeltaB = getSplitDelta( new Position( root, [ 3, 3, 3, 1 ] ), new Element( 'p' ), 11, baseVersion );
  139. const transformed = transform( splitDelta, splitDeltaB, context );
  140. baseVersion = splitDeltaB.operations.length;
  141. expect( transformed.length ).to.equal( 1 );
  142. expectDelta( transformed[ 0 ], {
  143. type: SplitDelta,
  144. operations: [
  145. {
  146. type: InsertOperation,
  147. position: new Position( root, [ 3, 3, 5 ] ),
  148. baseVersion
  149. },
  150. {
  151. type: MoveOperation,
  152. sourcePosition: new Position( root, [ 3, 3, 4, 2 ] ),
  153. howMany: 9,
  154. targetPosition: new Position( root, [ 3, 3, 5, 0 ] ),
  155. baseVersion: baseVersion + 1
  156. }
  157. ]
  158. } );
  159. // Test if deltas do what they should after applying transformed delta.
  160. applyDelta( splitDeltaB, doc );
  161. applyDelta( transformed[ 0 ], doc );
  162. const nodesAndText = getNodesAndText( Range.createFromPositionAndShift( new Position( root, [ 3, 3, 0 ] ), 6 ) );
  163. // P element is correctly split, there are three P elements, letters in P elements are in correct order.
  164. expect( nodesAndText ).to.equal( 'XXXXXabcdXPaPPbcPPfoobarxyzP' );
  165. } );
  166. it( 'split in same parent, incoming delta splits further, split deltas have reinsert operations', () => {
  167. let reOp = new ReinsertOperation(
  168. new Position( gy, [ 1 ] ),
  169. 1,
  170. Position.createFromPosition( splitDelta.operations[ 0 ].position ),
  171. splitDelta.operations[ 0 ].baseVersion
  172. );
  173. splitDelta.operations[ 0 ] = reOp;
  174. const splitDeltaB = getSplitDelta( new Position( root, [ 3, 3, 3, 1 ] ), new Element( 'p' ), 11, baseVersion );
  175. reOp = new ReinsertOperation(
  176. new Position( gy, [ 0 ] ),
  177. 1,
  178. Position.createFromPosition( splitDeltaB.operations[ 0 ].position ),
  179. splitDeltaB.operations[ 0 ].baseVersion
  180. );
  181. splitDeltaB.operations[ 0 ] = reOp;
  182. const transformed = transform( splitDelta, splitDeltaB, context );
  183. baseVersion = splitDeltaB.operations.length;
  184. expect( transformed.length ).to.equal( 1 );
  185. expectDelta( transformed[ 0 ], {
  186. type: SplitDelta,
  187. operations: [
  188. {
  189. type: ReinsertOperation,
  190. sourcePosition: new Position( gy, [ 0 ] ),
  191. howMany: 1,
  192. targetPosition: new Position( root, [ 3, 3, 5 ] ),
  193. baseVersion
  194. },
  195. {
  196. type: MoveOperation,
  197. sourcePosition: new Position( root, [ 3, 3, 4, 2 ] ),
  198. howMany: 9,
  199. targetPosition: new Position( root, [ 3, 3, 5, 0 ] ),
  200. baseVersion: baseVersion + 1
  201. }
  202. ]
  203. } );
  204. } );
  205. it( 'split in split parent', () => {
  206. const splitDeltaB = getSplitDelta( new Position( root, [ 3, 3, 3 ] ), new Element( 'div' ), 1, baseVersion );
  207. const transformed = transform( splitDelta, splitDeltaB, context );
  208. baseVersion = splitDeltaB.operations.length;
  209. expect( transformed.length ).to.equal( 1 );
  210. expectDelta( transformed[ 0 ], {
  211. type: SplitDelta,
  212. operations: [
  213. {
  214. type: InsertOperation,
  215. position: new Position( root, [ 3, 4, 1 ] ),
  216. baseVersion
  217. },
  218. {
  219. type: MoveOperation,
  220. sourcePosition: new Position( root, [ 3, 4, 0, 3 ] ),
  221. howMany: 9,
  222. targetPosition: new Position( root, [ 3, 4, 1, 0 ] ),
  223. baseVersion: baseVersion + 1
  224. }
  225. ]
  226. } );
  227. // Test if deltas do what they should after applying transformed delta.
  228. applyDelta( splitDeltaB, doc );
  229. applyDelta( transformed[ 0 ], doc );
  230. const nodesAndText = getNodesAndText( Range.createFromPositionAndShift( new Position( root, [ 3, 3 ] ), 2 ) );
  231. // DIV and P elements are correctly split.
  232. expect( nodesAndText ).to.equal( 'DIVXXXXXabcdXDIVDIVPabcPPfoobarxyzPDIV' );
  233. } );
  234. } );
  235. describe( 'UnwrapDelta', () => {
  236. it( 'split position directly in unwrapped node', () => {
  237. const unwrapDelta = getUnwrapDelta( new Position( root, [ 3, 3, 3 ] ), 12, baseVersion );
  238. const transformed = transform( splitDelta, unwrapDelta, context );
  239. baseVersion = unwrapDelta.operations.length;
  240. expect( transformed.length ).to.equal( 1 );
  241. expectDelta( transformed[ 0 ], {
  242. type: Delta,
  243. operations: [
  244. {
  245. type: NoOperation,
  246. baseVersion
  247. }
  248. ]
  249. } );
  250. // Test if deltas do what they should after applying transformed delta.
  251. applyDelta( unwrapDelta, doc );
  252. applyDelta( transformed[ 0 ], doc );
  253. const nodesAndText = getNodesAndText( Range.createFromPositionAndShift( new Position( root, [ 3, 3 ] ), 1 ) );
  254. // UnwrapDelta is applied. SplitDelta is discarded.
  255. expect( nodesAndText ).to.equal( 'DIVXXXXXabcdXabcfoobarxyzDIV' );
  256. } );
  257. it( 'split position indirectly in unwrapped node', () => {
  258. const unwrapDelta = getUnwrapDelta( new Position( root, [ 3, 3 ] ), 4, baseVersion );
  259. const transformed = transform( splitDelta, unwrapDelta, context );
  260. expect( transformed.length ).to.equal( 1 );
  261. baseVersion = unwrapDelta.operations.length;
  262. expectDelta( transformed[ 0 ], {
  263. type: SplitDelta,
  264. operations: [
  265. {
  266. type: InsertOperation,
  267. position: new Position( root, [ 3, 7 ] ),
  268. baseVersion
  269. },
  270. {
  271. type: MoveOperation,
  272. sourcePosition: new Position( root, [ 3, 6, 3 ] ),
  273. howMany: 9,
  274. targetPosition: new Position( root, [ 3, 7, 0 ] ),
  275. baseVersion: baseVersion + 1
  276. }
  277. ]
  278. } );
  279. // Test if deltas do what they should after applying transformed delta.
  280. applyDelta( unwrapDelta, doc );
  281. applyDelta( transformed[ 0 ], doc );
  282. const nodesAndText = getNodesAndText( Range.createFromPositionAndShift( new Position( root, [ 3 ] ), 1 ) );
  283. // UnwrapDelta and SplitDelta are correctly applied.
  284. expect( nodesAndText ).to.equal( 'DIVXXXXXaXXXXXXabcdXPabcPPfoobarxyzPDIV' );
  285. } );
  286. } );
  287. describe( 'WrapDelta', () => {
  288. it( 'split position is between wrapped nodes', () => {
  289. const wrapRange = new Range( new Position( root, [ 3, 3, 3, 1 ] ), new Position( root, [ 3, 3, 3, 5 ] ) );
  290. const wrapElement = new Element( 'E' );
  291. const wrapDelta = getWrapDelta( wrapRange, wrapElement, baseVersion );
  292. const transformed = transform( splitDelta, wrapDelta, context );
  293. baseVersion = wrapDelta.operations.length;
  294. expect( transformed.length ).to.equal( 1 );
  295. expectDelta( transformed[ 0 ], {
  296. type: Delta,
  297. operations: [
  298. {
  299. type: NoOperation,
  300. baseVersion
  301. }
  302. ]
  303. } );
  304. // Test if deltas do what they should after applying transformed delta.
  305. applyDelta( wrapDelta, doc );
  306. applyDelta( transformed[ 0 ], doc );
  307. const nodesAndText = getNodesAndText( Range.createFromPositionAndShift( new Position( root, [ 3, 3, 3 ] ), 1 ) );
  308. // WrapDelta is applied. SplitDelta is discarded.
  309. expect( nodesAndText ).to.equal( 'PaEbcfoEobarxyzP' );
  310. } );
  311. it( 'split position is before wrapped nodes', () => {
  312. const wrapRange = new Range( new Position( root, [ 3, 3, 3, 5 ] ), new Position( root, [ 3, 3, 3, 7 ] ) );
  313. const wrapElement = new Element( 'E' );
  314. const wrapDelta = getWrapDelta( wrapRange, wrapElement, baseVersion );
  315. const transformed = transform( splitDelta, wrapDelta, context );
  316. expect( transformed.length ).to.equal( 1 );
  317. baseVersion = wrapDelta.operations.length;
  318. expectDelta( transformed[ 0 ], {
  319. type: SplitDelta,
  320. operations: [
  321. {
  322. type: InsertOperation,
  323. position: new Position( root, [ 3, 3, 4 ] ),
  324. baseVersion
  325. },
  326. {
  327. type: MoveOperation,
  328. sourcePosition: new Position( root, [ 3, 3, 3, 3 ] ),
  329. howMany: 8,
  330. targetPosition: new Position( root, [ 3, 3, 4, 0 ] ),
  331. baseVersion: baseVersion + 1
  332. }
  333. ]
  334. } );
  335. // Test if deltas do what they should after applying transformed delta.
  336. applyDelta( wrapDelta, doc );
  337. applyDelta( transformed[ 0 ], doc );
  338. const nodesAndText = getNodesAndText( Range.createFromPositionAndShift( new Position( root, [ 3, 3, 3 ] ), 2 ) );
  339. // WrapDelta and SplitDelta are correctly applied.
  340. expect( nodesAndText ).to.equal( 'PabcPPfoEobEarxyzP' );
  341. } );
  342. it( 'split position is inside wrapped node', () => {
  343. const wrapRange = new Range( new Position( root, [ 3, 3, 2 ] ), new Position( root, [ 3, 3, 4 ] ) );
  344. const wrapElement = new Element( 'E' );
  345. const wrapDelta = getWrapDelta( wrapRange, wrapElement, baseVersion );
  346. const transformed = transform( splitDelta, wrapDelta, context );
  347. expect( transformed.length ).to.equal( 1 );
  348. baseVersion = wrapDelta.operations.length;
  349. expectDelta( transformed[ 0 ], {
  350. type: SplitDelta,
  351. operations: [
  352. {
  353. type: InsertOperation,
  354. position: new Position( root, [ 3, 3, 2, 2 ] ),
  355. baseVersion
  356. },
  357. {
  358. type: MoveOperation,
  359. sourcePosition: new Position( root, [ 3, 3, 2, 1, 3 ] ),
  360. howMany: 9,
  361. targetPosition: new Position( root, [ 3, 3, 2, 2, 0 ] ),
  362. baseVersion: baseVersion + 1
  363. }
  364. ]
  365. } );
  366. // Test if deltas do what they should after applying transformed delta.
  367. applyDelta( wrapDelta, doc );
  368. applyDelta( transformed[ 0 ], doc );
  369. const nodesAndText = getNodesAndText( Range.createFromPositionAndShift( new Position( root, [ 3, 3, 2 ] ), 1 ) );
  370. // WrapDelta and SplitDelta are correctly applied.
  371. expect( nodesAndText ).to.equal( 'EXabcdXPabcPPfoobarxyzPE' );
  372. } );
  373. } );
  374. describe( 'AttributeDelta', () => {
  375. it( 'attribute changed on split element', () => {
  376. const attributeDelta = new AttributeDelta();
  377. attributeDelta.addOperation( new AttributeOperation(
  378. Range.createFromParentsAndOffsets( root, 0, root, 2 ), 'key', 'oldValue', 'newValue', baseVersion
  379. ) );
  380. attributeDelta.addOperation( new AttributeOperation(
  381. Range.createFromPositionAndShift( new Position( root, [ 3, 3, 2 ] ), 3 ), 'key', null, 'newValue', baseVersion + 1
  382. ) );
  383. const transformed = transform( splitDelta, attributeDelta, context );
  384. baseVersion = attributeDelta.operations.length;
  385. expect( transformed.length ).to.equal( 1 );
  386. expectDelta( transformed[ 0 ], {
  387. type: SplitDelta,
  388. operations: [
  389. {
  390. type: InsertOperation,
  391. position: new Position( root, [ 3, 3, 4 ] ),
  392. baseVersion
  393. },
  394. {
  395. type: MoveOperation,
  396. sourcePosition: new Position( root, [ 3, 3, 3, 3 ] ),
  397. howMany: 9,
  398. targetPosition: new Position( root, [ 3, 3, 4, 0 ] ),
  399. baseVersion: baseVersion + 1
  400. }
  401. ]
  402. } );
  403. expect( transformed[ 0 ].operations[ 0 ].nodes.getNode( 0 ).getAttribute( 'key' ) ).to.equal( 'newValue' );
  404. } );
  405. it( 'attribute removed from split element', () => {
  406. splitDelta.operations[ 0 ].nodes.getNode( 0 ).setAttribute( 'key', 'oldValue' );
  407. const attributeDelta = new AttributeDelta();
  408. attributeDelta.addOperation( new AttributeOperation(
  409. Range.createFromParentsAndOffsets( root, 0, root, 2 ), 'key', 'otherValue', null, baseVersion
  410. ) );
  411. attributeDelta.addOperation( new AttributeOperation(
  412. Range.createFromPositionAndShift( new Position( root, [ 3, 3, 2 ] ), 3 ), 'key', 'oldValue', null, baseVersion + 1
  413. ) );
  414. const transformed = transform( splitDelta, attributeDelta, context );
  415. baseVersion = attributeDelta.operations.length;
  416. expect( transformed.length ).to.equal( 1 );
  417. expectDelta( transformed[ 0 ], {
  418. type: SplitDelta,
  419. operations: [
  420. {
  421. type: InsertOperation,
  422. position: new Position( root, [ 3, 3, 4 ] ),
  423. baseVersion
  424. },
  425. {
  426. type: MoveOperation,
  427. sourcePosition: new Position( root, [ 3, 3, 3, 3 ] ),
  428. howMany: 9,
  429. targetPosition: new Position( root, [ 3, 3, 4, 0 ] ),
  430. baseVersion: baseVersion + 1
  431. }
  432. ]
  433. } );
  434. expect( transformed[ 0 ].operations[ 0 ].nodes.getNode( 0 ).hasAttribute( 'key' ) ).to.be.false;
  435. } );
  436. it( 'attribute changed on split element that is reinserted from graveyard', () => {
  437. splitDelta.operations[ 0 ] = new ReinsertOperation(
  438. new Position( gy, [ 1 ] ),
  439. 1,
  440. new Position( root, [ 3, 3, 4 ] ),
  441. baseVersion
  442. );
  443. const attributeDelta = new AttributeDelta();
  444. attributeDelta.addOperation( new AttributeOperation(
  445. Range.createFromParentsAndOffsets( root, 0, root, 4 ), 'key', 'oldValue', 'newValue', baseVersion
  446. ) );
  447. const transformed = transform( splitDelta, attributeDelta, context );
  448. baseVersion = attributeDelta.operations.length;
  449. expect( transformed.length ).to.equal( 1 );
  450. expectDelta( transformed[ 0 ], {
  451. type: SplitDelta,
  452. operations: [
  453. {
  454. type: ReinsertOperation,
  455. sourcePosition: new Position( gy, [ 1 ] ),
  456. howMany: 1,
  457. targetPosition: new Position( root, [ 3, 3, 4 ] ),
  458. baseVersion
  459. },
  460. {
  461. type: MoveOperation,
  462. sourcePosition: new Position( root, [ 3, 3, 3, 3 ] ),
  463. howMany: 9,
  464. targetPosition: new Position( root, [ 3, 3, 4, 0 ] ),
  465. baseVersion: baseVersion + 1
  466. }
  467. ]
  468. } );
  469. } );
  470. } );
  471. describe( 'RenameDelta', () => {
  472. it( 'renamed split element', () => {
  473. const renameDelta = new RenameDelta();
  474. renameDelta.addOperation( new RenameOperation(
  475. new Position( root, [ 3, 3, 3 ] ), 'p', 'li', baseVersion
  476. ) );
  477. const transformed = transform( splitDelta, renameDelta, context );
  478. baseVersion = renameDelta.operations.length;
  479. expect( transformed.length ).to.equal( 1 );
  480. expectDelta( transformed[ 0 ], {
  481. type: SplitDelta,
  482. operations: [
  483. {
  484. type: InsertOperation,
  485. position: new Position( root, [ 3, 3, 4 ] ),
  486. baseVersion
  487. },
  488. {
  489. type: MoveOperation,
  490. sourcePosition: new Position( root, [ 3, 3, 3, 3 ] ),
  491. howMany: 9,
  492. targetPosition: new Position( root, [ 3, 3, 4, 0 ] ),
  493. baseVersion: baseVersion + 1
  494. }
  495. ]
  496. } );
  497. expect( transformed[ 0 ].operations[ 0 ].nodes.getNode( 0 ).name ).to.equal( 'li' );
  498. } );
  499. it( 'split element is different than renamed element', () => {
  500. const renameDelta = new RenameDelta();
  501. renameDelta.addOperation( new RenameOperation(
  502. new Position( root, [ 4 ] ), 'p', 'li', baseVersion
  503. ) );
  504. const transformed = transform( splitDelta, renameDelta, context );
  505. baseVersion = renameDelta.operations.length;
  506. expect( transformed.length ).to.equal( 1 );
  507. expectDelta( transformed[ 0 ], {
  508. type: SplitDelta,
  509. operations: [
  510. {
  511. type: InsertOperation,
  512. position: new Position( root, [ 3, 3, 4 ] ),
  513. baseVersion
  514. },
  515. {
  516. type: MoveOperation,
  517. sourcePosition: new Position( root, [ 3, 3, 3, 3 ] ),
  518. howMany: 9,
  519. targetPosition: new Position( root, [ 3, 3, 4, 0 ] ),
  520. baseVersion: baseVersion + 1
  521. }
  522. ]
  523. } );
  524. } );
  525. it( 'renamed split element that is reinserted from graveyard', () => {
  526. splitDelta.operations[ 0 ] = new ReinsertOperation(
  527. new Position( gy, [ 1 ] ),
  528. 1,
  529. new Position( root, [ 3, 3, 4 ] ),
  530. baseVersion
  531. );
  532. const renameDelta = new RenameDelta();
  533. renameDelta.addOperation( new RenameOperation(
  534. new Position( root, [ 3, 3, 3 ] ), 'p', 'li', baseVersion
  535. ) );
  536. const transformed = transform( splitDelta, renameDelta, context );
  537. baseVersion = renameDelta.operations.length;
  538. expect( transformed.length ).to.equal( 1 );
  539. expectDelta( transformed[ 0 ], {
  540. type: SplitDelta,
  541. operations: [
  542. {
  543. type: ReinsertOperation,
  544. sourcePosition: new Position( gy, [ 1 ] ),
  545. howMany: 1,
  546. targetPosition: new Position( root, [ 3, 3, 4 ] ),
  547. baseVersion
  548. },
  549. {
  550. type: MoveOperation,
  551. sourcePosition: new Position( root, [ 3, 3, 3, 3 ] ),
  552. howMany: 9,
  553. targetPosition: new Position( root, [ 3, 3, 4, 0 ] ),
  554. baseVersion: baseVersion + 1
  555. }
  556. ]
  557. } );
  558. } );
  559. } );
  560. describe( 'RemoveDelta', () => {
  561. it( 'node inside the removed range was a node that has been split', () => {
  562. splitPosition = new Position( root, [ 3, 3, 2, 2 ] );
  563. splitDelta = getSplitDelta( splitPosition, new Element( 'x' ), 2, baseVersion );
  564. const removePosition = new Position( root, [ 3, 3, 1 ] );
  565. const removeDelta = getRemoveDelta( removePosition, 3, baseVersion );
  566. const removeOperation = removeDelta.operations[ 0 ];
  567. const transformed = transform( splitDelta, removeDelta, context );
  568. expect( transformed.length ).to.equal( 1 );
  569. baseVersion = removeDelta.operations.length;
  570. const newInsertPosition = removeOperation.targetPosition.getShiftedBy( 2 );
  571. const newMoveSourcePosition = removeOperation.targetPosition.getShiftedBy( 1 );
  572. newMoveSourcePosition.path.push( 2 );
  573. const newMoveTargetPosition = Position.createAt( newInsertPosition );
  574. newMoveTargetPosition.path.push( 0 );
  575. expectDelta( transformed[ 0 ], {
  576. type: SplitDelta,
  577. operations: [
  578. {
  579. type: InsertOperation,
  580. position: newInsertPosition,
  581. baseVersion
  582. },
  583. {
  584. type: MoveOperation,
  585. sourcePosition: newMoveSourcePosition,
  586. howMany: 2,
  587. targetPosition: newMoveTargetPosition,
  588. baseVersion: baseVersion + 1
  589. }
  590. ]
  591. } );
  592. } );
  593. it( 'last node in the removed range was a node that has been split', () => {
  594. const removePosition = new Position( root, [ 3, 3, 2 ] );
  595. const removeDelta = getRemoveDelta( removePosition, 2, baseVersion );
  596. const removeOperation = removeDelta.operations[ 0 ];
  597. const transformed = transform( splitDelta, removeDelta, context );
  598. expect( transformed.length ).to.equal( 1 );
  599. baseVersion = removeDelta.operations.length;
  600. const newInsertPosition = removeOperation.targetPosition.getShiftedBy( 2 );
  601. const newMoveSourcePosition = removeOperation.targetPosition.getShiftedBy( 1 );
  602. newMoveSourcePosition.path.push( 3 );
  603. const newMoveTargetPosition = Position.createAt( newInsertPosition );
  604. newMoveTargetPosition.path.push( 0 );
  605. expectDelta( transformed[ 0 ], {
  606. type: SplitDelta,
  607. operations: [
  608. {
  609. type: InsertOperation,
  610. position: newInsertPosition,
  611. baseVersion
  612. },
  613. {
  614. type: MoveOperation,
  615. sourcePosition: newMoveSourcePosition,
  616. howMany: 9,
  617. targetPosition: newMoveTargetPosition,
  618. baseVersion: baseVersion + 1
  619. }
  620. ]
  621. } );
  622. } );
  623. } );
  624. } );
  625. } );