8
0

moveoperation.js 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: document */
  6. /* bender-include: ../../_tools/tools.js */
  7. /* global describe, before, beforeEach, it, expect */
  8. 'use strict';
  9. const modules = bender.amd.require(
  10. 'document/document',
  11. 'document/operation/moveoperation',
  12. 'document/operation/insertoperation',
  13. 'document/operation/changeoperation',
  14. 'document/operation/nooperation',
  15. 'document/attribute',
  16. 'document/position',
  17. 'document/range',
  18. 'document/element',
  19. 'document/node',
  20. 'document/nodelist',
  21. 'ckeditorerror'
  22. );
  23. describe( 'MoveOperation', () => {
  24. let Document, MoveOperation, InsertOperation, ChangeOperation, NoOperation,
  25. Attribute, Position, Range, Element, Node, NodeList, CKEditorError;
  26. before( () => {
  27. Document = modules[ 'document/document' ];
  28. MoveOperation = modules[ 'document/operation/moveoperation' ];
  29. InsertOperation = modules[ 'document/operation/insertoperation' ];
  30. ChangeOperation = modules[ 'document/operation/changeoperation' ];
  31. NoOperation = modules[ 'document/operation/nooperation' ];
  32. Attribute = modules[ 'document/attribute' ];
  33. Position = modules[ 'document/position' ];
  34. Range = modules[ 'document/range' ];
  35. Element = modules[ 'document/element' ];
  36. Node = modules[ 'document/node' ];
  37. NodeList = modules[ 'document/nodelist' ];
  38. CKEditorError = modules.ckeditorerror;
  39. } );
  40. let doc, root;
  41. beforeEach( () => {
  42. doc = new Document();
  43. root = doc.createRoot( 'root' );
  44. } );
  45. it( 'should move from one node to another', () => {
  46. let p1 = new Element( 'p1', [], new Element( 'x' ) );
  47. let p2 = new Element( 'p2' );
  48. root.insertChildren( 0, [ p1, p2 ] );
  49. doc.applyOperation(
  50. new MoveOperation(
  51. new Position( [ 0, 0 ], root ),
  52. new Position( [ 1, 0 ], root ),
  53. 1,
  54. doc.version
  55. )
  56. );
  57. expect( doc.version ).to.equal( 1 );
  58. expect( root.getChildCount() ).to.equal( 2 );
  59. expect( root.getChild( 0 ).name ).to.equal( 'p1' );
  60. expect( root.getChild( 1 ).name ).to.equal( 'p2' );
  61. expect( p1.getChildCount() ).to.equal( 0 );
  62. expect( p2.getChildCount() ).to.equal( 1 );
  63. expect( p2.getChild( 0 ).name ).to.equal( 'x' );
  64. } );
  65. it( 'should move position of children in one node backward', () => {
  66. root.insertChildren( 0, 'xbarx' );
  67. doc.applyOperation(
  68. new MoveOperation(
  69. new Position( [ 2 ], root ),
  70. new Position( [ 1 ], root ),
  71. 2,
  72. doc.version
  73. )
  74. );
  75. expect( doc.version ).to.equal( 1 );
  76. expect( root.getChildCount() ).to.equal( 5 );
  77. expect( root.getChild( 0 ).character ).to.equal( 'x' );
  78. expect( root.getChild( 1 ).character ).to.equal( 'a' );
  79. expect( root.getChild( 2 ).character ).to.equal( 'r' );
  80. expect( root.getChild( 3 ).character ).to.equal( 'b' );
  81. expect( root.getChild( 4 ).character ).to.equal( 'x' );
  82. } );
  83. it( 'should move position of children in one node forward', () => {
  84. root.insertChildren( 0, 'xbarx' );
  85. doc.applyOperation(
  86. new MoveOperation(
  87. new Position( [ 1 ], root ),
  88. new Position( [ 4 ], root ),
  89. 2,
  90. doc.version
  91. )
  92. );
  93. expect( doc.version ).to.equal( 1 );
  94. expect( root.getChildCount() ).to.equal( 5 );
  95. expect( root.getChild( 0 ).character ).to.equal( 'x' );
  96. expect( root.getChild( 1 ).character ).to.equal( 'r' );
  97. expect( root.getChild( 2 ).character ).to.equal( 'b' );
  98. expect( root.getChild( 3 ).character ).to.equal( 'a' );
  99. expect( root.getChild( 4 ).character ).to.equal( 'x' );
  100. } );
  101. it( 'should create a move operation as a reverse', () => {
  102. let nodeList = new NodeList( 'bar' );
  103. let sourcePosition = new Position( [ 0 ], root );
  104. let targetPosition = new Position( [ 4 ], root );
  105. let operation = new MoveOperation( sourcePosition, targetPosition, nodeList.length, doc.version );
  106. let reverse = operation.getReversed();
  107. expect( reverse ).to.be.an.instanceof( MoveOperation );
  108. expect( reverse.baseVersion ).to.equal( 1 );
  109. expect( reverse.howMany ).to.equal( nodeList.length );
  110. expect( reverse.sourcePosition.isEqual( targetPosition ) ).to.be.true;
  111. expect( reverse.targetPosition.isEqual( sourcePosition ) ).to.be.true;
  112. } );
  113. it( 'should undo move node by applying reverse operation', () => {
  114. let p1 = new Element( 'p1', [], new Element( 'x' ) );
  115. let p2 = new Element( 'p2' );
  116. root.insertChildren( 0, [ p1, p2 ] );
  117. let operation = new MoveOperation(
  118. new Position( [ 0, 0 ], root ),
  119. new Position( [ 1, 0 ], root ),
  120. 1,
  121. doc.version
  122. );
  123. doc.applyOperation( operation );
  124. expect( doc.version ).to.equal( 1 );
  125. expect( root.getChildCount() ).to.equal( 2 );
  126. expect( p1.getChildCount() ).to.equal( 0 );
  127. expect( p2.getChildCount() ).to.equal( 1 );
  128. expect( p2.getChild( 0 ).name ).to.equal( 'x' );
  129. doc.applyOperation( operation.getReversed() );
  130. expect( doc.version ).to.equal( 2 );
  131. expect( root.getChildCount() ).to.equal( 2 );
  132. expect( p1.getChildCount() ).to.equal( 1 );
  133. expect( p1.getChild( 0 ).name ).to.equal( 'x' );
  134. expect( p2.getChildCount() ).to.equal( 0 );
  135. } );
  136. it( 'should throw an error if number of nodes to move exceeds the number of existing nodes in given element', () => {
  137. root.insertChildren( 0, 'xbarx' );
  138. let operation = new MoveOperation(
  139. new Position( [ 3 ], root ),
  140. new Position( [ 1 ], root ),
  141. 3,
  142. doc.version
  143. );
  144. expect( () => doc.applyOperation( operation ) ).to.throw( CKEditorError, /operation-move-nodes-do-not-exist/ );
  145. } );
  146. it( 'should throw an error if target or source parent-element specified by position does not exist', () => {
  147. let p = new Element( 'p' );
  148. p.insertChildren( 0, 'foo' );
  149. root.insertChildren( 0, [ 'ab', p ] );
  150. let operation = new MoveOperation(
  151. new Position( [ 2, 0 ], root ),
  152. new Position( [ 1 ], root ),
  153. 3,
  154. doc.version
  155. );
  156. root.removeChildren( 2, 1 );
  157. expect( () => doc.applyOperation( operation ) ).to.throw( CKEditorError, /operation-move-position-invalid/ );
  158. } );
  159. it( 'should throw an error if operation tries to move a range between the beginning and the end of that range', () => {
  160. root.insertChildren( 0, 'xbarx' );
  161. let operation = new MoveOperation(
  162. new Position( [ 1 ], root ),
  163. new Position( [ 2 ], root ),
  164. 3,
  165. doc.version
  166. );
  167. expect( () => doc.applyOperation( operation ) ).to.throw( CKEditorError, /operation-move-range-into-itself/ );
  168. } );
  169. it( 'should throw an error if operation tries to move a range into a sub-tree of a node that is in that range', () => {
  170. let p = new Element( 'p', [], [ new Element( 'p' ) ] );
  171. root.insertChildren( 0, [ 'ab', p, 'xy' ] );
  172. let operation = new MoveOperation(
  173. new Position( [ 1 ], root ),
  174. new Position( [ 2, 0, 0 ], root ),
  175. 3,
  176. doc.version
  177. );
  178. expect( () => doc.applyOperation( operation ) ).to.throw( CKEditorError, /operation-move-node-into-itself/ );
  179. } );
  180. it( 'should not throw an error if operation move a range into a sibling', () => {
  181. let p = new Element( 'p' );
  182. root.insertChildren( 0, [ 'ab', p, 'xy' ] );
  183. let operation = new MoveOperation(
  184. new Position( [ 1 ], root ),
  185. new Position( [ 2, 0 ], root ),
  186. 1,
  187. doc.version
  188. );
  189. expect(
  190. () => {
  191. doc.applyOperation( operation );
  192. }
  193. ).not.to.throw();
  194. expect( root.getChildCount() ).to.equal( 4 );
  195. expect( p.getChildCount() ).to.equal( 1 );
  196. expect( p.getChild( 0 ).character ).to.equal( 'b' );
  197. } );
  198. it( 'should create operation with the same parameters when cloned', () => {
  199. let sourcePosition = new Position( [ 0 ], root );
  200. let targetPosition = new Position( [ 1 ], root );
  201. let howMany = 4;
  202. let baseVersion = doc.version;
  203. let op = new MoveOperation( sourcePosition, targetPosition, howMany, baseVersion );
  204. let clone = op.clone();
  205. // New instance rather than a pointer to the old instance.
  206. expect( clone ).not.to.be.equal( op );
  207. expect( clone ).to.be.instanceof( MoveOperation );
  208. expect( clone.sourcePosition.isEqual( sourcePosition ) ).to.be.true;
  209. expect( clone.targetPosition.isEqual( targetPosition ) ).to.be.true;
  210. expect( clone.howMany ).to.equal( howMany );
  211. expect( clone.baseVersion ).to.equal( baseVersion );
  212. } );
  213. describe( 'getTransformedBy', () => {
  214. let nodeA, nodeB, op, baseVersion, sourcePosition, targetPosition, rangeEnd, howMany, expected;
  215. let expectOperation;
  216. beforeEach( () => {
  217. nodeA = new Node();
  218. nodeB = new Node();
  219. baseVersion = doc.version;
  220. sourcePosition = new Position( [ 2, 2, 4 ], root );
  221. targetPosition = new Position( [ 3, 3, 3 ], root );
  222. howMany = 2;
  223. rangeEnd = sourcePosition.clone();
  224. rangeEnd.offset += howMany;
  225. op = new MoveOperation( sourcePosition, targetPosition, howMany, baseVersion );
  226. expected = {
  227. type: MoveOperation,
  228. sourcePosition: sourcePosition.clone(),
  229. targetPosition: targetPosition.clone(),
  230. howMany: howMany,
  231. baseVersion: baseVersion + 1
  232. };
  233. expectOperation = bender.tools.operations.expectOperation( Position, Range );
  234. } );
  235. describe( 'InsertOperation', () => {
  236. it( 'target at different position than move range and target: no operation update', () => {
  237. let transformBy = new InsertOperation(
  238. new Position( [ 1, 3, 2 ], root ),
  239. [ nodeA, nodeB ],
  240. baseVersion
  241. );
  242. let transOp = op.getTransformedBy( transformBy );
  243. expectOperation( transOp[ 0 ], expected );
  244. } );
  245. it( 'target inside node from move range: no operation update', () => {
  246. let transformBy = new InsertOperation(
  247. new Position( [ 2, 2, 3, 1 ], root ),
  248. [ nodeA, nodeB ],
  249. baseVersion
  250. );
  251. let transOp = op.getTransformedBy( transformBy );
  252. expectOperation( transOp[ 0 ], expected );
  253. } );
  254. it( 'target at offset before range offset: increment range offset', () => {
  255. let transformBy = new InsertOperation(
  256. new Position( [ 2, 2, 0 ], root ),
  257. [ nodeA, nodeB ],
  258. baseVersion
  259. );
  260. let transOp = op.getTransformedBy( transformBy );
  261. expected.sourcePosition.offset += 2;
  262. expectOperation( transOp[ 0 ], expected );
  263. } );
  264. it( 'target at offset after range offset: no operation update', () => {
  265. let transformBy = new InsertOperation(
  266. new Position( [ 2, 2, 6 ], root ),
  267. [ nodeA, nodeB ],
  268. baseVersion
  269. );
  270. let transOp = op.getTransformedBy( transformBy );
  271. expectOperation( transOp[ 0 ], expected );
  272. } );
  273. it( 'target before node from range start path: increment index on range start path', () => {
  274. let transformBy = new InsertOperation(
  275. new Position( [ 2, 0 ], root ),
  276. [ nodeA, nodeB ],
  277. baseVersion
  278. );
  279. let transOp = op.getTransformedBy( transformBy );
  280. expected.sourcePosition.path[ 1 ] += 2;
  281. expectOperation( transOp[ 0 ], expected );
  282. } );
  283. it( 'target after node from range start path: no operation update', () => {
  284. let transformBy = new InsertOperation(
  285. new Position( [ 2, 3 ], root ),
  286. [ nodeA, nodeB ],
  287. baseVersion
  288. );
  289. let transOp = op.getTransformedBy( transformBy );
  290. expectOperation( transOp[ 0 ], expected );
  291. } );
  292. it( 'target offset before move target offset: increment target offset', () => {
  293. let transformBy = new InsertOperation(
  294. new Position( [ 3, 3, 2 ], root ),
  295. [ nodeA, nodeB ],
  296. baseVersion
  297. );
  298. let transOp = op.getTransformedBy( transformBy );
  299. expected.targetPosition.offset += 2;
  300. expectOperation( transOp[ 0 ], expected );
  301. } );
  302. it( 'target offset after move target offset: no operation update', () => {
  303. let transformBy = new InsertOperation(
  304. new Position( [ 3, 3, 4 ], root ),
  305. [ nodeA, nodeB ],
  306. baseVersion
  307. );
  308. let transOp = op.getTransformedBy( transformBy );
  309. expectOperation( transOp[ 0 ], expected );
  310. } );
  311. it( 'target before node from move target position path: increment index on move target position path', () => {
  312. let transformBy = new InsertOperation(
  313. new Position( [ 3, 0 ], root ),
  314. [ nodeA, nodeB ],
  315. baseVersion
  316. );
  317. let transOp = op.getTransformedBy( transformBy );
  318. expected.targetPosition.path[ 1 ] += 2;
  319. expectOperation( transOp[ 0 ], expected );
  320. } );
  321. it( 'target after node from move target position path: no operation update', () => {
  322. let transformBy = new InsertOperation(
  323. new Position( [ 3, 6 ], root ),
  324. [ nodeA, nodeB ],
  325. baseVersion
  326. );
  327. let transOp = op.getTransformedBy( transformBy );
  328. expectOperation( transOp[ 0 ], expected );
  329. } );
  330. it( 'target offset same as move target offset and is important: increment target offset', () => {
  331. let transformBy = new InsertOperation(
  332. new Position( [ 3, 3, 3 ], root ),
  333. [ nodeA, nodeB ],
  334. baseVersion
  335. );
  336. let transOp = op.getTransformedBy( transformBy );
  337. expected.targetPosition.offset += 2;
  338. expectOperation( transOp[ 0 ], expected );
  339. } );
  340. it( 'target offset same as move target offset and is less important: no operation update', () => {
  341. let transformBy = new InsertOperation(
  342. new Position( [ 3, 3, 3 ], root ),
  343. [ nodeA, nodeB ],
  344. baseVersion
  345. );
  346. let transOp = op.getTransformedBy( transformBy, true );
  347. expectOperation( transOp[ 0 ], expected );
  348. } );
  349. it( 'target offset same as move target offset and is important: increment target offset', () => {
  350. let transformBy = new InsertOperation(
  351. new Position( [ 3, 3 ], root ),
  352. [ nodeA, nodeB ],
  353. baseVersion
  354. );
  355. let transOp = op.getTransformedBy( transformBy );
  356. expected.targetPosition.path[ 1 ] += 2;
  357. expectOperation( transOp[ 0 ], expected );
  358. } );
  359. it( 'target inside move range: split into two operations', () => {
  360. let transformBy = new InsertOperation(
  361. new Position( [ 2, 2, 5 ], root ),
  362. [ nodeA, nodeB ],
  363. baseVersion
  364. );
  365. let transOp = op.getTransformedBy( transformBy );
  366. expect( transOp ).to.be.instanceof( Array );
  367. expect( transOp.length ).to.equal( 2 );
  368. expected.sourcePosition.path = [ 2, 2, 7 ];
  369. expected.howMany = 1;
  370. expectOperation( transOp[ 0 ], expected );
  371. expected.sourcePosition.path = [ 2, 2, 4 ];
  372. expected.howMany = 1;
  373. expected.baseVersion++;
  374. expectOperation( transOp[ 1 ], expected );
  375. } );
  376. } );
  377. describe( 'ChangeOperation', () => {
  378. it( 'no operation update', () => {
  379. let transformBy = new ChangeOperation(
  380. new Range( sourcePosition, rangeEnd ),
  381. new Attribute( 'abc', true ),
  382. new Attribute( 'abc', false ),
  383. baseVersion
  384. );
  385. let transOp = op.getTransformedBy( transformBy );
  386. expectOperation( transOp[ 0 ], expected );
  387. } );
  388. } );
  389. describe( 'MoveOperation', () => {
  390. it( 'range and target different than transforming range and target: no operation update', () => {
  391. let transformBy = new MoveOperation(
  392. new Position( [ 1, 2 ], root ),
  393. new Position( [ 4, 1, 0 ], root ),
  394. 3,
  395. baseVersion
  396. );
  397. let transOp = op.getTransformedBy( transformBy );
  398. expectOperation( transOp[ 0 ], expected );
  399. } );
  400. it( 'target offset before transforming range start offset: increment range offset', () => {
  401. let transformBy = new MoveOperation(
  402. new Position( [ 4, 1, 0 ], root ),
  403. new Position( [ 2, 2, 0 ], root ),
  404. 2,
  405. baseVersion
  406. );
  407. let transOp = op.getTransformedBy( transformBy );
  408. expected.sourcePosition.offset += 2;
  409. expectOperation( transOp[ 0 ], expected );
  410. } );
  411. it( 'target offset after transforming range start offset: no operation update', () => {
  412. let transformBy = new MoveOperation(
  413. new Position( [ 4, 1, 0 ], root ),
  414. new Position( [ 2, 2, 7 ], root ),
  415. 2,
  416. baseVersion
  417. );
  418. let transOp = op.getTransformedBy( transformBy );
  419. expectOperation( transOp[ 0 ], expected );
  420. } );
  421. it( 'range start offset before transforming range start offset: decrement range offset', () => {
  422. let transformBy = new MoveOperation(
  423. new Position( [ 2, 2, 0 ], root ),
  424. new Position( [ 4, 1, 0 ], root ),
  425. 2,
  426. baseVersion
  427. );
  428. let transOp = op.getTransformedBy( transformBy );
  429. expected.sourcePosition.offset -= 2;
  430. expectOperation( transOp[ 0 ], expected );
  431. } );
  432. it( 'range start offset after transforming range start offset: no operation update', () => {
  433. let transformBy = new MoveOperation(
  434. new Position( [ 2, 2, 9 ], root ),
  435. new Position( [ 4, 1, 0 ], root ),
  436. 2,
  437. baseVersion
  438. );
  439. let transOp = op.getTransformedBy( transformBy, true );
  440. expectOperation( transOp[ 0 ], expected );
  441. } );
  442. it( 'target before node from transforming range start path: increment index on range start path', () => {
  443. let transformBy = new MoveOperation(
  444. new Position( [ 4, 1, 0 ], root ),
  445. new Position( [ 2, 1 ], root ),
  446. 2,
  447. baseVersion
  448. );
  449. let transOp = op.getTransformedBy( transformBy );
  450. expected.sourcePosition.path[ 1 ] += 2;
  451. expectOperation( transOp[ 0 ], expected );
  452. } );
  453. it( 'target after node from transforming range start path: no operation update', () => {
  454. let transformBy = new MoveOperation(
  455. new Position( [ 4, 1, 0 ], root ),
  456. new Position( [ 2, 5 ], root ),
  457. 2,
  458. baseVersion
  459. );
  460. let transOp = op.getTransformedBy( transformBy );
  461. expectOperation( transOp[ 0 ], expected );
  462. } );
  463. it( 'range before node from transforming range start path: decrement index on range start path', () => {
  464. let transformBy = new MoveOperation(
  465. new Position( [ 2, 0 ], root ),
  466. new Position( [ 4, 1, 0 ], root ),
  467. 2,
  468. baseVersion
  469. );
  470. let transOp = op.getTransformedBy( transformBy );
  471. expected.sourcePosition.path[ 1 ] -= 2;
  472. expectOperation( transOp[ 0 ], expected );
  473. } );
  474. it( 'range after node from transforming range start path: no operation update', () => {
  475. let transformBy = new MoveOperation(
  476. new Position( [ 2, 3 ], root ),
  477. new Position( [ 4, 1, 0 ], root ),
  478. 2,
  479. baseVersion
  480. );
  481. let transOp = op.getTransformedBy( transformBy );
  482. expectOperation( transOp[ 0 ], expected );
  483. } );
  484. it( 'target offset before transforming target offset: increment target offset', () => {
  485. let transformBy = new MoveOperation(
  486. new Position( [ 4, 1, 0 ], root ),
  487. new Position( [ 3, 3, 0 ], root ),
  488. 2,
  489. baseVersion
  490. );
  491. let transOp = op.getTransformedBy( transformBy );
  492. expected.targetPosition.offset += 2;
  493. expectOperation( transOp[ 0 ], expected );
  494. } );
  495. it( 'target offset after transforming target offset: no operation update', () => {
  496. let transformBy = new MoveOperation(
  497. new Position( [ 4, 1, 0 ], root ),
  498. new Position( [ 3, 3, 5 ], root ),
  499. 2,
  500. baseVersion
  501. );
  502. let transOp = op.getTransformedBy( transformBy );
  503. expectOperation( transOp[ 0 ], expected );
  504. } );
  505. it( 'range offset before transforming target offset: decrement target offset', () => {
  506. let transformBy = new MoveOperation(
  507. new Position( [ 3, 3, 0 ], root ),
  508. new Position( [ 4, 1, 0 ], root ),
  509. 2,
  510. baseVersion
  511. );
  512. let transOp = op.getTransformedBy( transformBy );
  513. expected.targetPosition.offset -= 2;
  514. expectOperation( transOp[ 0 ], expected );
  515. } );
  516. it( 'range offset after transforming target offset: no operation update', () => {
  517. let transformBy = new MoveOperation(
  518. new Position( [ 3, 3, 5 ], root ),
  519. new Position( [ 4, 1, 0 ], root ),
  520. 2,
  521. baseVersion
  522. );
  523. let transOp = op.getTransformedBy( transformBy );
  524. expectOperation( transOp[ 0 ], expected );
  525. } );
  526. it( 'target before node from transforming target path: increment index on target path', () => {
  527. let transformBy = new MoveOperation(
  528. new Position( [ 4, 1, 0 ], root ),
  529. new Position( [ 3, 1 ], root ),
  530. 2,
  531. baseVersion
  532. );
  533. let transOp = op.getTransformedBy( transformBy );
  534. expected.targetPosition.path[ 1 ] += 2;
  535. expectOperation( transOp[ 0 ], expected );
  536. } );
  537. it( 'target after node from transforming target path: no operation update', () => {
  538. let transformBy = new MoveOperation(
  539. new Position( [ 4, 1, 0 ], root ),
  540. new Position( [ 3, 5 ], root ),
  541. 2,
  542. baseVersion
  543. );
  544. let transOp = op.getTransformedBy( transformBy );
  545. expectOperation( transOp[ 0 ], expected );
  546. } );
  547. it( 'range before node from transforming target path: decrement index on target path', () => {
  548. let transformBy = new MoveOperation(
  549. new Position( [ 3, 0 ], root ),
  550. new Position( [ 4, 1, 0 ], root ),
  551. 2,
  552. baseVersion
  553. );
  554. let transOp = op.getTransformedBy( transformBy );
  555. expected.targetPosition.path[ 1 ] -= 2;
  556. expectOperation( transOp[ 0 ], expected );
  557. } );
  558. it( 'range after node from transforming target path: no operation update', () => {
  559. let transformBy = new MoveOperation(
  560. new Position( [ 3, 5 ], root ),
  561. new Position( [ 4, 1, 0 ], root ),
  562. 2,
  563. baseVersion
  564. );
  565. let transOp = op.getTransformedBy( transformBy );
  566. expectOperation( transOp[ 0 ], expected );
  567. } );
  568. it( 'target inside transforming move range: split into two operations', () => {
  569. let transformBy = new MoveOperation(
  570. new Position( [ 4, 1, 0 ], root ),
  571. new Position( [ 2, 2, 5 ], root ),
  572. 2,
  573. baseVersion
  574. );
  575. let transOp = op.getTransformedBy( transformBy );
  576. expect( transOp ).to.be.instanceof( Array );
  577. expect( transOp.length ).to.equal( 2 );
  578. expected.howMany = 1;
  579. expected.sourcePosition.offset = 7;
  580. expectOperation( transOp[ 0 ], expected );
  581. expected.sourcePosition.offset = 4;
  582. expected.baseVersion++;
  583. expectOperation( transOp[ 1 ], expected );
  584. } );
  585. it( 'target inside a node from transforming range: no operation update', () => {
  586. let transformBy = new MoveOperation(
  587. new Position( [ 4, 1, 0 ], root ),
  588. new Position( [ 2, 2, 5, 1 ], root ),
  589. 2,
  590. baseVersion
  591. );
  592. let transOp = op.getTransformedBy( transformBy );
  593. expectOperation( transOp[ 0 ], expected );
  594. } );
  595. it( 'range has node that contains transforming range: update range path', () => {
  596. let transformBy = new MoveOperation(
  597. new Position( [ 2, 1 ], root ),
  598. new Position( [ 4, 2 ], root ),
  599. 3,
  600. baseVersion
  601. );
  602. let transOp = op.getTransformedBy( transformBy );
  603. expected.sourcePosition.path = [ 4, 3, 4 ];
  604. expectOperation( transOp[ 0 ], expected );
  605. } );
  606. it( 'range has node that contains transforming target: update target path', () => {
  607. let transformBy = new MoveOperation(
  608. new Position( [ 3, 2 ], root ),
  609. new Position( [ 0, 1 ], root ),
  610. 3,
  611. baseVersion
  612. );
  613. let transOp = op.getTransformedBy( transformBy );
  614. expected.targetPosition.path = [ 0, 2, 3 ];
  615. expectOperation( transOp[ 0 ], expected );
  616. } );
  617. it( 'target inside a node from transforming range and vice versa: reverse transform-by operation', () => {
  618. let transformBy = new MoveOperation(
  619. new Position( [ 3, 2 ], root ),
  620. new Position( [ 2, 2, 5, 0 ], root ),
  621. 3,
  622. baseVersion
  623. );
  624. let transOp = op.getTransformedBy( transformBy );
  625. let reversed = transformBy.getReversed();
  626. expected.sourcePosition = reversed.sourcePosition;
  627. expected.targetPosition = reversed.targetPosition;
  628. expected.howMany = reversed.howMany;
  629. expectOperation( transOp[ 0 ], expected );
  630. } );
  631. it( 'range is same as transforming range and is important: convert to NoOperation', () => {
  632. let transformBy = new MoveOperation(
  633. op.sourcePosition.clone(),
  634. new Position( [ 4, 1, 0 ], root ),
  635. op.howMany,
  636. baseVersion
  637. );
  638. let transOp = op.getTransformedBy( transformBy );
  639. expectOperation( transOp[ 0 ], {
  640. type: NoOperation,
  641. baseVersion: baseVersion + 1
  642. } );
  643. } );
  644. it( 'range is same as transforming range and is less important: update range path', () => {
  645. let transformBy = new MoveOperation(
  646. op.sourcePosition.clone(),
  647. new Position( [ 4, 1, 0 ], root ),
  648. op.howMany,
  649. baseVersion
  650. );
  651. let transOp = op.getTransformedBy( transformBy, true );
  652. expected.sourcePosition.path = [ 4, 1, 0 ];
  653. expectOperation( transOp[ 0 ], expected );
  654. } );
  655. it( 'range contains transforming range and is important: convert to NoOperation', () => {
  656. let transformBy = new MoveOperation(
  657. new Position( [ 2, 2, 3 ], root ),
  658. new Position( [ 4, 1, 0 ], root ),
  659. 4,
  660. baseVersion
  661. );
  662. let transOp = op.getTransformedBy( transformBy );
  663. expectOperation( transOp[ 0 ], {
  664. type: NoOperation,
  665. baseVersion: baseVersion + 1
  666. } );
  667. } );
  668. it( 'range contains transforming range and is less important: update range path', () => {
  669. let transformBy = new MoveOperation(
  670. new Position( [ 2, 2, 3 ], root ),
  671. new Position( [ 4, 1, 0 ], root ),
  672. 4,
  673. baseVersion
  674. );
  675. let transOp = op.getTransformedBy( transformBy, true );
  676. expected.sourcePosition.path = [ 4, 1, 1 ];
  677. expectOperation( transOp[ 0 ], expected );
  678. } );
  679. it( 'range intersects on left with transforming range and is important: shrink range', () => {
  680. let transformBy = new MoveOperation(
  681. new Position( [ 2, 2, 3 ], root ),
  682. new Position( [ 4, 1, 0 ], root ),
  683. 2,
  684. baseVersion
  685. );
  686. let transOp = op.getTransformedBy( transformBy );
  687. expected.sourcePosition.path = [ 2, 2, 3 ];
  688. expected.howMany = 1;
  689. expectOperation( transOp[ 0 ], expected );
  690. } );
  691. it( 'range intersects on left with transforming range and is less important: split into two operations', () => {
  692. let transformBy = new MoveOperation(
  693. new Position( [ 2, 2, 3 ], root ),
  694. new Position( [ 4, 1, 0 ], root ),
  695. 2,
  696. baseVersion
  697. );
  698. let transOp = op.getTransformedBy( transformBy, true );
  699. expect( transOp ).to.be.instanceof( Array );
  700. expect( transOp.length ).to.equal( 2 );
  701. expected.sourcePosition.path = [ 2, 2, 3 ];
  702. expectOperation( transOp[ 0 ], {
  703. type: MoveOperation,
  704. sourcePosition: new Position( [ 4, 1, 1 ], root ),
  705. targetPosition: expected.targetPosition,
  706. howMany: 1,
  707. baseVersion: expected.baseVersion
  708. } );
  709. expected.howMany = 1;
  710. expected.baseVersion++;
  711. expectOperation( transOp[ 1 ], expected );
  712. } );
  713. it( 'range intersects on right with transforming range and is important: shrink range', () => {
  714. let transformBy = new MoveOperation(
  715. new Position( [ 2, 2, 5 ], root ),
  716. new Position( [ 4, 1, 0 ], root ),
  717. 2,
  718. baseVersion
  719. );
  720. let transOp = op.getTransformedBy( transformBy );
  721. expected.howMany = 1;
  722. expectOperation( transOp[ 0 ], expected );
  723. } );
  724. it( 'range intersects on right with transforming range and is less important: split into two operations', () => {
  725. let transformBy = new MoveOperation(
  726. new Position( [ 2, 2, 5 ], root ),
  727. new Position( [ 4, 1, 0 ], root ),
  728. 2,
  729. baseVersion
  730. );
  731. let transOp = op.getTransformedBy( transformBy, true );
  732. expect( transOp ).to.be.instanceof( Array );
  733. expect( transOp.length ).to.equal( 2 );
  734. expectOperation( transOp[ 0 ], {
  735. type: MoveOperation,
  736. sourcePosition: new Position( [ 4, 1, 0 ], root ),
  737. targetPosition: expected.targetPosition,
  738. howMany: 1,
  739. baseVersion: expected.baseVersion
  740. } );
  741. expected.howMany = 1;
  742. expected.baseVersion++;
  743. expectOperation( transOp[ 1 ], expected );
  744. } );
  745. it( 'range inside transforming range and is important: shrink range', () => {
  746. op.howMany = 4;
  747. let transformBy = new MoveOperation(
  748. new Position( [ 2, 2, 5 ], root ),
  749. new Position( [ 4, 1, 0 ], root ),
  750. 2,
  751. baseVersion
  752. );
  753. let transOp = op.getTransformedBy( transformBy );
  754. expected.howMany = 2;
  755. expectOperation( transOp[ 0 ], expected );
  756. } );
  757. it( 'range inside transforming range and is less important: split into two operations', () => {
  758. op.howMany = 4;
  759. let transformBy = new MoveOperation(
  760. new Position( [ 2, 2, 5 ], root ),
  761. new Position( [ 4, 1, 0 ], root ),
  762. 2,
  763. baseVersion
  764. );
  765. let transOp = op.getTransformedBy( transformBy, true );
  766. expect( transOp ).to.be.instanceof( Array );
  767. expect( transOp.length ).to.equal( 2 );
  768. expectOperation( transOp[ 0 ], {
  769. type: MoveOperation,
  770. sourcePosition: new Position( [ 4, 1, 0 ], root ),
  771. targetPosition: expected.targetPosition,
  772. howMany: 2,
  773. baseVersion: expected.baseVersion
  774. } );
  775. expected.howMany = 2;
  776. expected.baseVersion++;
  777. expectOperation( transOp[ 1 ], expected );
  778. } );
  779. it( 'range and target inside transforming range and is less important: split into three operations', () => {
  780. op.howMany = 6;
  781. let transformBy = new MoveOperation(
  782. new Position( [ 2, 2, 5 ], root ),
  783. new Position( [ 2, 2, 9 ], root ),
  784. 2,
  785. baseVersion
  786. );
  787. let transOp = op.getTransformedBy( transformBy, true );
  788. expect( transOp ).to.be.instanceof( Array );
  789. expect( transOp.length ).to.equal( 3 );
  790. expected.sourcePosition.path = [ 2, 2, 9 ];
  791. expected.howMany = 1;
  792. expectOperation( transOp[ 0 ], expected );
  793. expected.sourcePosition.path = [ 2, 2, 7 ];
  794. expected.howMany = 2;
  795. expected.baseVersion++;
  796. expectOperation( transOp[ 1 ], expected );
  797. expected.sourcePosition.path = [ 2, 2, 4 ];
  798. expected.howMany = 3;
  799. expected.baseVersion++;
  800. expectOperation( transOp[ 2 ], expected );
  801. } );
  802. } );
  803. describe( 'NoOperation', () => {
  804. it( 'no operation update', () => {
  805. let transformBy = new NoOperation( baseVersion );
  806. let transOp = op.getTransformedBy( transformBy );
  807. expectOperation( transOp[ 0 ], expected );
  808. } );
  809. } );
  810. } );
  811. } );