splitdelta.js 24 KB

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