transform.js 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import transform from '../../../src/model/operation/transform';
  6. import Model from '../../../src/model/model';
  7. import RootElement from '../../../src/model/rootelement';
  8. import Node from '../../../src/model/node';
  9. import Position from '../../../src/model/position';
  10. import Range from '../../../src/model/range';
  11. import InsertOperation from '../../../src/model/operation/insertoperation';
  12. import AttributeOperation from '../../../src/model/operation/attributeoperation';
  13. import RootAttributeOperation from '../../../src/model/operation/rootattributeoperation';
  14. import MarkerOperation from '../../../src/model/operation/markeroperation';
  15. import MoveOperation from '../../../src/model/operation/moveoperation';
  16. import RenameOperation from '../../../src/model/operation/renameoperation';
  17. import NoOperation from '../../../src/model/operation/nooperation';
  18. describe( 'transform', () => {
  19. let model, doc, root, op, nodeA, nodeB, expected;
  20. beforeEach( () => {
  21. model = new Model();
  22. doc = model.document;
  23. root = doc.createRoot();
  24. nodeA = new Node();
  25. nodeB = new Node();
  26. } );
  27. function expectOperation( op, params ) {
  28. for ( const i in params ) {
  29. if ( params.hasOwnProperty( i ) ) {
  30. if ( i == 'type' ) {
  31. expect( op, 'type' ).to.be.instanceof( params[ i ] );
  32. }
  33. else if ( params[ i ] instanceof Array ) {
  34. expect( op[ i ].length, i ).to.equal( params[ i ].length );
  35. for ( let j = 0; j < params[ i ].length; j++ ) {
  36. expect( op[ i ][ j ] ).to.equal( params[ i ][ j ] );
  37. }
  38. } else if ( params[ i ] instanceof Position || params[ i ] instanceof Range ) {
  39. expect( op[ i ].isEqual( params[ i ] ), i ).to.be.true;
  40. } else {
  41. expect( op[ i ], i ).to.equal( params[ i ] );
  42. }
  43. }
  44. }
  45. }
  46. describe( 'InsertOperation', () => {
  47. let nodeC, nodeD, position;
  48. beforeEach( () => {
  49. nodeC = new Node();
  50. nodeD = new Node();
  51. position = new Position( root, [ 0, 2, 2 ] );
  52. op = new InsertOperation( position, [ nodeA, nodeB ], 0 );
  53. expected = {
  54. type: InsertOperation,
  55. position: Position.createFromPosition( position )
  56. };
  57. } );
  58. describe( 'by InsertOperation', () => {
  59. it( 'target at different position: no position update', () => {
  60. const transformBy = new InsertOperation(
  61. new Position( root, [ 1, 3, 2 ] ),
  62. [ nodeC, nodeD ],
  63. 0
  64. );
  65. const transOp = transform.transform( op, transformBy );
  66. expect( transOp.length ).to.equal( 1 );
  67. expectOperation( transOp[ 0 ], expected );
  68. } );
  69. it( 'target at offset before: increment offset', () => {
  70. const transformBy = new InsertOperation(
  71. new Position( root, [ 0, 2, 0 ] ),
  72. [ nodeC, nodeD ],
  73. 0
  74. );
  75. const transOp = transform.transform( op, transformBy );
  76. expected.position.offset += 2;
  77. expect( transOp.length ).to.equal( 1 );
  78. expectOperation( transOp[ 0 ], expected );
  79. } );
  80. it( 'target at same offset and is important: increment offset', () => {
  81. const transformBy = new InsertOperation(
  82. new Position( root, [ 0, 2, 2 ] ),
  83. [ nodeC, nodeD ],
  84. 0
  85. );
  86. const transOp = transform.transform( op, transformBy );
  87. expected.position.offset += 2;
  88. expect( transOp.length ).to.equal( 1 );
  89. expectOperation( transOp[ 0 ], expected );
  90. } );
  91. it( 'target at same offset and is less important: no position update', () => {
  92. const transformBy = new InsertOperation(
  93. new Position( root, [ 0, 2, 2 ] ),
  94. [ nodeC, nodeD ],
  95. 0
  96. );
  97. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  98. expect( transOp.length ).to.equal( 1 );
  99. expectOperation( transOp[ 0 ], expected );
  100. } );
  101. it( 'target at same offset and context.insertBefore = false: increment offset', () => {
  102. const transformBy = new InsertOperation(
  103. new Position( root, [ 0, 2, 2 ] ),
  104. [ nodeC, nodeD ],
  105. 0
  106. );
  107. const transOp = transform.transform( op, transformBy );
  108. expected.position.offset += 2;
  109. expect( transOp.length ).to.equal( 1 );
  110. expectOperation( transOp[ 0 ], expected );
  111. } );
  112. it( 'target at offset after: no position update', () => {
  113. const transformBy = new InsertOperation(
  114. new Position( root, [ 0, 2, 3 ] ),
  115. [ nodeC, nodeD ],
  116. 0
  117. );
  118. const transOp = transform.transform( op, transformBy );
  119. expect( transOp.length ).to.equal( 1 );
  120. expectOperation( transOp[ 0 ], expected );
  121. } );
  122. it( 'target before node from path: increment index on path', () => {
  123. const transformBy = new InsertOperation(
  124. new Position( root, [ 0, 1 ] ),
  125. [ nodeC, nodeD ],
  126. 0
  127. );
  128. const transOp = transform.transform( op, transformBy );
  129. expected.position.path[ 1 ] += 2;
  130. expect( transOp.length ).to.equal( 1 );
  131. expectOperation( transOp[ 0 ], expected );
  132. } );
  133. it( 'target after node from path: no position update', () => {
  134. const transformBy = new InsertOperation(
  135. new Position( root, [ 0, 6 ] ),
  136. [ nodeC, nodeD ],
  137. 0
  138. );
  139. const transOp = transform.transform( op, transformBy );
  140. expect( transOp.length ).to.equal( 1 );
  141. expectOperation( transOp[ 0 ], expected );
  142. } );
  143. } );
  144. describe( 'by AttributeOperation', () => {
  145. it( 'no position update', () => {
  146. const transformBy = new AttributeOperation(
  147. Range.createFromPositionAndShift( position, 2 ),
  148. 'foo',
  149. null,
  150. 'bar',
  151. 0
  152. );
  153. const transOp = transform.transform( op, transformBy );
  154. expect( transOp.length ).to.equal( 1 );
  155. expectOperation( transOp[ 0 ], expected );
  156. } );
  157. } );
  158. describe( 'by RootAttributeOperation', () => {
  159. it( 'no position update', () => {
  160. const transformBy = new RootAttributeOperation(
  161. root,
  162. 'foo',
  163. null,
  164. 'bar',
  165. 0
  166. );
  167. const transOp = transform.transform( op, transformBy );
  168. expect( transOp.length ).to.equal( 1 );
  169. expectOperation( transOp[ 0 ], expected );
  170. } );
  171. } );
  172. describe( 'by MoveOperation', () => {
  173. it( 'range and target are different than insert position: no position update', () => {
  174. const transformBy = new MoveOperation(
  175. new Position( root, [ 1, 3, 2 ] ),
  176. 2,
  177. new Position( root, [ 2, 1 ] ),
  178. 0
  179. );
  180. const transOp = transform.transform( op, transformBy );
  181. expect( transOp.length ).to.equal( 1 );
  182. expectOperation( transOp[ 0 ], expected );
  183. } );
  184. it( 'range is before insert position offset: decrement offset', () => {
  185. const transformBy = new MoveOperation(
  186. new Position( root, [ 0, 2, 0 ] ),
  187. 1,
  188. new Position( root, [ 1, 1 ] ),
  189. 0
  190. );
  191. const transOp = transform.transform( op, transformBy );
  192. expected.position.offset--;
  193. expect( transOp.length ).to.equal( 1 );
  194. expectOperation( transOp[ 0 ], expected );
  195. } );
  196. it( 'range offset is after insert position offset: no position update', () => {
  197. const transformBy = new MoveOperation(
  198. new Position( root, [ 0, 2, 5 ] ),
  199. 1,
  200. new Position( root, [ 1, 1 ] ),
  201. 0
  202. );
  203. const transOp = transform.transform( op, transformBy );
  204. expect( transOp.length ).to.equal( 1 );
  205. expectOperation( transOp[ 0 ], expected );
  206. } );
  207. it( 'target offset before insert position offset: increment offset', () => {
  208. const transformBy = new MoveOperation(
  209. new Position( root, [ 1, 1 ] ),
  210. 2,
  211. new Position( root, [ 0, 2, 0 ] ),
  212. 0
  213. );
  214. const transOp = transform.transform( op, transformBy );
  215. expected.position.offset += 2;
  216. expect( transOp.length ).to.equal( 1 );
  217. expectOperation( transOp[ 0 ], expected );
  218. } );
  219. it( 'target offset after insert position offset: no position update', () => {
  220. const transformBy = new MoveOperation(
  221. new Position( root, [ 1, 1 ] ),
  222. 2,
  223. new Position( root, [ 0, 2, 5 ] ),
  224. 0
  225. );
  226. const transOp = transform.transform( op, transformBy );
  227. expect( transOp.length ).to.equal( 1 );
  228. expectOperation( transOp[ 0 ], expected );
  229. } );
  230. it( 'target offset same as insert position offset and is important: increment offset', () => {
  231. const transformBy = new MoveOperation(
  232. new Position( root, [ 1, 1 ] ),
  233. 2,
  234. new Position( root, [ 0, 2, 2 ] ),
  235. 0
  236. );
  237. const transOp = transform.transform( op, transformBy );
  238. expected.position.offset += 2;
  239. expect( transOp.length ).to.equal( 1 );
  240. expectOperation( transOp[ 0 ], expected );
  241. } );
  242. it( 'target offset same as insert position offset and is less important: increment offset', () => {
  243. const transformBy = new MoveOperation(
  244. new Position( root, [ 1, 1 ] ),
  245. 2,
  246. new Position( root, [ 0, 2, 2 ] ),
  247. 0
  248. );
  249. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  250. expected.position.offset += 2;
  251. expect( transOp.length ).to.equal( 1 );
  252. expectOperation( transOp[ 0 ], expected );
  253. } );
  254. it( 'range is before node from insert position path: decrement index on path', () => {
  255. const transformBy = new MoveOperation(
  256. new Position( root, [ 0, 0 ] ),
  257. 1,
  258. new Position( root, [ 1, 0 ] ),
  259. 0
  260. );
  261. const transOp = transform.transform( op, transformBy );
  262. expected.position.path[ 1 ] -= 1;
  263. expect( transOp.length ).to.equal( 1 );
  264. expectOperation( transOp[ 0 ], expected );
  265. } );
  266. it( 'range is after node from insert position path: no position update', () => {
  267. const transformBy = new MoveOperation(
  268. new Position( root, [ 0, 4 ] ),
  269. 2,
  270. new Position( root, [ 1, 0 ] ),
  271. 0
  272. );
  273. const transOp = transform.transform( op, transformBy );
  274. expect( transOp.length ).to.equal( 1 );
  275. expectOperation( transOp[ 0 ], expected );
  276. } );
  277. it( 'target before node from insert position path: increment index on path', () => {
  278. const transformBy = new MoveOperation(
  279. new Position( root, [ 1, 0 ] ),
  280. 2,
  281. new Position( root, [ 0, 0 ] ),
  282. 0
  283. );
  284. const transOp = transform.transform( op, transformBy );
  285. expected.position.path[ 1 ] += 2;
  286. expect( transOp.length ).to.equal( 1 );
  287. expectOperation( transOp[ 0 ], expected );
  288. } );
  289. it( 'target after node from insert position path: no position update', () => {
  290. const transformBy = new MoveOperation(
  291. new Position( root, [ 1, 0 ] ),
  292. 2,
  293. new Position( root, [ 0, 4 ] ),
  294. 0
  295. );
  296. const transOp = transform.transform( op, transformBy );
  297. expect( transOp.length ).to.equal( 1 );
  298. expectOperation( transOp[ 0 ], expected );
  299. } );
  300. it( 'range has node that contains insert position: update position', () => {
  301. const transformBy = new MoveOperation(
  302. new Position( root, [ 0, 1 ] ),
  303. 2,
  304. new Position( root, [ 1, 1 ] ),
  305. 0
  306. );
  307. const transOp = transform.transform( op, transformBy );
  308. expected.position.path = [ 1, 2, 2 ];
  309. expect( transOp.length ).to.equal( 1 );
  310. expectOperation( transOp[ 0 ], expected );
  311. } );
  312. it( 'range contains insert position (on same level): update position', () => {
  313. const transformBy = new MoveOperation(
  314. new Position( root, [ 0, 2, 0 ] ),
  315. 4,
  316. new Position( root, [ 1, 0 ] ),
  317. 0
  318. );
  319. const transOp = transform.transform( op, transformBy );
  320. expected.position.path = [ 1, 2 ];
  321. expect( transOp.length ).to.equal( 1 );
  322. expectOperation( transOp[ 0 ], expected );
  323. } );
  324. } );
  325. describe( 'by NoOperation', () => {
  326. it( 'no operation update', () => {
  327. const transformBy = new NoOperation( 0 );
  328. const transOp = transform.transform( op, transformBy );
  329. expect( transOp.length ).to.equal( 1 );
  330. expectOperation( transOp[ 0 ], expected );
  331. } );
  332. } );
  333. describe( 'by RenameOperation', () => {
  334. it( 'no position update', () => {
  335. const transformBy = new RenameOperation( new Position( root, [ 0, 2, 0 ] ), 'oldName', 'newName', 0 );
  336. const transOp = transform.transform( op, transformBy );
  337. expect( transOp.length ).to.equal( 1 );
  338. expectOperation( transOp[ 0 ], expected );
  339. } );
  340. } );
  341. describe( 'by MarkerOperation', () => {
  342. it( 'no position update', () => {
  343. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 4 ] ) );
  344. const transformBy = new MarkerOperation( 'name', null, newRange, model.markers, 0 );
  345. const transOp = transform.transform( op, transformBy );
  346. expect( transOp.length ).to.equal( 1 );
  347. expectOperation( transOp[ 0 ], expected );
  348. } );
  349. } );
  350. } );
  351. describe( 'AttributeOperation', () => {
  352. let start, end, range;
  353. beforeEach( () => {
  354. start = new Position( root, [ 0, 2, 1 ] );
  355. end = new Position( root, [ 0, 2, 4 ] );
  356. range = new Range( start, end );
  357. op = new AttributeOperation( range, 'foo', 'abc', 'bar', 0 );
  358. expected = {
  359. type: AttributeOperation,
  360. range,
  361. key: 'foo',
  362. oldValue: 'abc',
  363. newValue: 'bar'
  364. };
  365. } );
  366. describe( 'by InsertOperation', () => {
  367. it( 'target at offset before: increment offset', () => {
  368. const transformBy = new InsertOperation(
  369. new Position( root, [ 0, 2, 0 ] ),
  370. [ nodeA, nodeB ],
  371. 0
  372. );
  373. const transOp = transform.transform( op, transformBy );
  374. expected.range.start.offset += 2;
  375. expected.range.end.offset += 2;
  376. expect( transOp.length ).to.equal( 1 );
  377. expectOperation( transOp[ 0 ], expected );
  378. } );
  379. it( 'target at same offset: increment offset', () => {
  380. const transformBy = new InsertOperation(
  381. new Position( root, [ 0, 2, 1 ] ),
  382. [ nodeA, nodeB ],
  383. 0
  384. );
  385. const transOp = transform.transform( op, transformBy );
  386. expected.range.start.offset += 2;
  387. expected.range.end.offset += 2;
  388. expect( transOp.length ).to.equal( 1 );
  389. expectOperation( transOp[ 0 ], expected );
  390. } );
  391. } );
  392. describe( 'by MoveOperation', () => {
  393. it( 'range offset is before change range start offset: decrement offset', () => {
  394. const transformBy = new MoveOperation(
  395. new Position( root, [ 0, 2, 0 ] ),
  396. 1,
  397. new Position( root, [ 2, 4, 1 ] ),
  398. 0
  399. );
  400. const transOp = transform.transform( op, transformBy );
  401. expected.range.start.offset--;
  402. expected.range.end.offset--;
  403. expect( transOp.length ).to.equal( 1 );
  404. expectOperation( transOp[ 0 ], expected );
  405. } );
  406. it( 'target offset is before change range start offset: increment offset', () => {
  407. const transformBy = new MoveOperation(
  408. new Position( root, [ 2, 4, 1 ] ),
  409. 2,
  410. new Position( root, [ 0, 2, 0 ] ),
  411. 0
  412. );
  413. const transOp = transform.transform( op, transformBy );
  414. expected.range.start.offset += 2;
  415. expected.range.end.offset += 2;
  416. expect( transOp.length ).to.equal( 1 );
  417. expectOperation( transOp[ 0 ], expected );
  418. } );
  419. it( 'range intersects on left with change range: split into two operations', () => {
  420. const transformBy = new MoveOperation(
  421. new Position( root, [ 0, 2, 2 ] ),
  422. 4,
  423. new Position( root, [ 2, 4, 1 ] ),
  424. 0
  425. );
  426. const transOp = transform.transform( op, transformBy );
  427. expect( transOp.length ).to.equal( 2 );
  428. expected.range.end.offset -= 2;
  429. expectOperation( transOp[ 0 ], expected );
  430. expected.range.start.path = [ 2, 4, 1 ];
  431. expected.range.end.path = [ 2, 4, 3 ];
  432. expectOperation( transOp[ 1 ], expected );
  433. } );
  434. it( 'range intersects on right with change range: split into two operation', () => {
  435. const transformBy = new MoveOperation(
  436. new Position( root, [ 0, 2, 0 ] ),
  437. 2,
  438. new Position( root, [ 2, 4, 1 ] ),
  439. 0
  440. );
  441. const transOp = transform.transform( op, transformBy );
  442. expect( transOp.length ).to.equal( 2 );
  443. expected.range.start.offset -= 1;
  444. expected.range.end.offset -= 2;
  445. expectOperation( transOp[ 0 ], expected );
  446. expected.range.start.path = [ 2, 4, 2 ];
  447. expected.range.end.path = [ 2, 4, 3 ];
  448. expectOperation( transOp[ 1 ], expected );
  449. } );
  450. it( 'range contains change range: update change range', () => {
  451. const transformBy = new MoveOperation(
  452. new Position( root, [ 0, 1 ] ),
  453. 3,
  454. new Position( root, [ 2, 4, 1 ] ),
  455. 0
  456. );
  457. const transOp = transform.transform( op, transformBy );
  458. expected.range.start.path = [ 2, 4, 2, 1 ];
  459. expected.range.end.path = [ 2, 4, 2, 4 ];
  460. expect( transOp.length ).to.equal( 1 );
  461. expectOperation( transOp[ 0 ], expected );
  462. } );
  463. it( 'range is inside change range: split into two operations', () => {
  464. const transformBy = new MoveOperation(
  465. new Position( root, [ 0, 2, 2 ] ),
  466. 1,
  467. new Position( root, [ 2, 4, 1 ] ),
  468. 0
  469. );
  470. const transOp = transform.transform( op, transformBy );
  471. expect( transOp.length ).to.equal( 3 );
  472. expected.range.start.path = [ 0, 2, 1 ];
  473. expected.range.end.path = [ 0, 2, 2 ];
  474. expectOperation( transOp[ 0 ], expected );
  475. expected.range.start.path = [ 0, 2, 2 ];
  476. expected.range.end.path = [ 0, 2, 3 ];
  477. expectOperation( transOp[ 1 ], expected );
  478. expected.range.start.path = [ 2, 4, 1 ];
  479. expected.range.end.path = [ 2, 4, 2 ];
  480. expectOperation( transOp[ 2 ], expected );
  481. } );
  482. it( 'range is same as change range: update change range', () => {
  483. const transformBy = new MoveOperation(
  484. new Position( root, [ 0, 2, 1 ] ),
  485. 3,
  486. new Position( root, [ 2, 4, 1 ] ),
  487. 0
  488. );
  489. const transOp = transform.transform( op, transformBy );
  490. expected.range.start.path = [ 2, 4, 1 ];
  491. expected.range.end.path = [ 2, 4, 4 ];
  492. expect( transOp.length ).to.equal( 1 );
  493. expectOperation( transOp[ 0 ], expected );
  494. } );
  495. it( 'target inside change range: split into two operations', () => {
  496. const transformBy = new MoveOperation(
  497. new Position( root, [ 2, 4, 1 ] ),
  498. 2,
  499. new Position( root, [ 0, 2, 2 ] ),
  500. 0
  501. );
  502. const transOp = transform.transform( op, transformBy );
  503. expect( transOp.length ).to.equal( 2 );
  504. expected.range.end.offset = 2;
  505. expectOperation( transOp[ 0 ], expected );
  506. expected.range.start.offset = 4;
  507. expected.range.end.offset = 6;
  508. expectOperation( transOp[ 1 ], expected );
  509. } );
  510. it( 'range intersects change range and target inside change range: split into three operations', () => {
  511. const transformBy = new MoveOperation(
  512. new Position( root, [ 0, 2, 0 ] ),
  513. 2,
  514. new Position( root, [ 0, 2, 3 ] ),
  515. 0
  516. );
  517. const transOp = transform.transform( op, transformBy );
  518. expect( transOp.length ).to.equal( 3 );
  519. expected.range.start.path = [ 0, 2, 0 ];
  520. expected.range.end.path = [ 0, 2, 1 ];
  521. expectOperation( transOp[ 0 ], expected );
  522. expected.range.start.path = [ 0, 2, 1 ];
  523. expected.range.end.path = [ 0, 2, 2 ];
  524. expectOperation( transOp[ 1 ], expected );
  525. expected.range.start.path = [ 0, 2, 2 ];
  526. expected.range.end.path = [ 0, 2, 3 ];
  527. expectOperation( transOp[ 2 ], expected );
  528. } );
  529. } );
  530. describe( 'by MoveOperation to graveyard', () => {
  531. beforeEach( () => {
  532. start = new Position( doc.graveyard, [ 2, 0 ] );
  533. end = new Position( doc.graveyard, [ 2, 4 ] );
  534. range = new Range( start, end );
  535. op = new AttributeOperation( range, 'foo', 'abc', 'bar', 0 );
  536. expected.range = new Range( start, end );
  537. } );
  538. it( 'remove operation inserted elements before attribute operation range: increment path', () => {
  539. const transformBy = new MoveOperation(
  540. new Position( root, [ 0 ] ),
  541. 2,
  542. new Position( doc.graveyard, [ 0 ] ),
  543. 0
  544. );
  545. transformBy.targetPosition.path = [ 0 ];
  546. const transOp = transform.transform( op, transformBy );
  547. expect( transOp.length ).to.equal( 1 );
  548. expected.range.start.path = [ 4, 0 ];
  549. expected.range.end.path = [ 4, 4 ];
  550. expectOperation( transOp[ 0 ], expected );
  551. } );
  552. it( 'remove operation inserted elements after attribute operation range: do nothing', () => {
  553. const transformBy = new MoveOperation(
  554. new Position( root, [ 0 ] ),
  555. 2,
  556. new Position( doc.graveyard, [ 0 ] ),
  557. 0
  558. );
  559. transformBy.targetPosition.path = [ 4 ];
  560. const transOp = transform.transform( op, transformBy );
  561. expect( transOp.length ).to.equal( 1 );
  562. expectOperation( transOp[ 0 ], expected );
  563. } );
  564. } );
  565. } );
  566. describe( 'RootAttributeOperation', () => {
  567. const diffRoot = new RootElement( null );
  568. beforeEach( () => {
  569. expected = {
  570. type: RootAttributeOperation,
  571. key: 'foo',
  572. oldValue: 'abc',
  573. newValue: 'bar'
  574. };
  575. op = new RootAttributeOperation( root, 'foo', 'abc', 'bar', 0 );
  576. } );
  577. describe( 'by InsertOperation', () => {
  578. it( 'no operation update', () => {
  579. const transformBy = new InsertOperation(
  580. new Position( root, [ 0 ] ),
  581. 'a',
  582. 0
  583. );
  584. const transOp = transform.transform( op, transformBy );
  585. expect( transOp.length ).to.equal( 1 );
  586. expectOperation( transOp[ 0 ], expected );
  587. } );
  588. } );
  589. describe( 'by AttributeOperation', () => {
  590. it( 'no operation update', () => {
  591. const transformBy = new AttributeOperation(
  592. new Range(
  593. new Position( root, [ 0 ] ),
  594. new Position( root, [ 1 ] )
  595. ),
  596. 'foo',
  597. 'bar',
  598. 'xyz',
  599. 0
  600. );
  601. const transOp = transform.transform( op, transformBy );
  602. expect( transOp.length ).to.equal( 1 );
  603. expectOperation( transOp[ 0 ], expected );
  604. } );
  605. } );
  606. describe( 'by RootAttributeOperation', () => {
  607. it( 'changes different root: no operation update', () => {
  608. const transformBy = new RootAttributeOperation(
  609. diffRoot,
  610. 'foo',
  611. 'abc',
  612. 'xyz',
  613. 0
  614. );
  615. const transOp = transform.transform( op, transformBy );
  616. expect( transOp.length ).to.equal( 1 );
  617. expectOperation( transOp[ 0 ], expected );
  618. } );
  619. it( 'changes different key: no operation update', () => {
  620. const transformBy = new RootAttributeOperation(
  621. root,
  622. 'abc',
  623. 'abc',
  624. 'xyz',
  625. 0
  626. );
  627. const transOp = transform.transform( op, transformBy );
  628. expect( transOp.length ).to.equal( 1 );
  629. expectOperation( transOp[ 0 ], expected );
  630. } );
  631. it( 'sets same value for same key: convert to NoOperation', () => {
  632. const transformBy = new RootAttributeOperation(
  633. root,
  634. 'foo',
  635. 'abc',
  636. 'bar',
  637. 0
  638. );
  639. const transOp = transform.transform( op, transformBy );
  640. expect( transOp.length ).to.equal( 1 );
  641. expectOperation( transOp[ 0 ], {
  642. type: NoOperation
  643. } );
  644. } );
  645. it( 'sets different value for same key on same root and is important: convert to NoOperation', () => {
  646. const transformBy = new RootAttributeOperation(
  647. root,
  648. 'foo',
  649. 'abc',
  650. 'xyz',
  651. 0
  652. );
  653. const transOp = transform.transform( op, transformBy );
  654. expect( transOp.length ).to.equal( 1 );
  655. expectOperation( transOp[ 0 ], {
  656. type: NoOperation
  657. } );
  658. } );
  659. it( 'sets different value for same key on same root and is less important: change oldValue', () => {
  660. const transformBy = new RootAttributeOperation(
  661. root,
  662. 'foo',
  663. 'abc',
  664. 'xyz',
  665. 0
  666. );
  667. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  668. expected.oldValue = 'xyz';
  669. expect( transOp.length ).to.equal( 1 );
  670. expectOperation( transOp[ 0 ], expected );
  671. } );
  672. } );
  673. describe( 'by MoveOperation', () => {
  674. it( 'no operation update', () => {
  675. const transformBy = new MoveOperation(
  676. new Position( root, [ 0 ] ),
  677. 2,
  678. new Position( root, [ 1 ] ),
  679. 0
  680. );
  681. const transOp = transform.transform( op, transformBy );
  682. expect( transOp.length ).to.equal( 1 );
  683. expectOperation( transOp[ 0 ], expected );
  684. } );
  685. } );
  686. describe( 'by NoOperation', () => {
  687. it( 'no operation update', () => {
  688. const transformBy = new NoOperation( 0 );
  689. const transOp = transform.transform( op, transformBy );
  690. expect( transOp.length ).to.equal( 1 );
  691. expectOperation( transOp[ 0 ], expected );
  692. } );
  693. } );
  694. describe( 'by RenameOperation', () => {
  695. it( 'no position update', () => {
  696. const transformBy = new RenameOperation( new Position( root, [ 0 ] ), 'oldName', 'newName', 0 );
  697. const transOp = transform.transform( op, transformBy );
  698. expect( transOp.length ).to.equal( 1 );
  699. expectOperation( transOp[ 0 ], expected );
  700. } );
  701. } );
  702. describe( 'by MarkerOperation', () => {
  703. it( 'no position update', () => {
  704. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 8 ] ) );
  705. const transformBy = new MarkerOperation( 'name', null, newRange, model.markers, 0 );
  706. const transOp = transform.transform( op, transformBy );
  707. expect( transOp.length ).to.equal( 1 );
  708. expectOperation( transOp[ 0 ], expected );
  709. } );
  710. } );
  711. } );
  712. describe( 'MoveOperation', () => {
  713. let sourcePosition, targetPosition, rangeEnd, howMany;
  714. beforeEach( () => {
  715. sourcePosition = new Position( root, [ 2, 2, 4 ] );
  716. targetPosition = new Position( root, [ 3, 3, 3 ] );
  717. howMany = 2;
  718. rangeEnd = Position.createFromPosition( sourcePosition );
  719. rangeEnd.offset += howMany;
  720. op = new MoveOperation( sourcePosition, howMany, targetPosition, 0 );
  721. expected = {
  722. type: MoveOperation,
  723. sourcePosition: Position.createFromPosition( sourcePosition ),
  724. targetPosition: Position.createFromPosition( targetPosition ),
  725. howMany
  726. };
  727. } );
  728. describe( 'by InsertOperation', () => {
  729. it( 'target at different position than move range and target: no operation update', () => {
  730. const transformBy = new InsertOperation(
  731. new Position( root, [ 1, 3, 2 ] ),
  732. [ nodeA, nodeB ],
  733. 0
  734. );
  735. const transOp = transform.transform( op, transformBy );
  736. expect( transOp.length ).to.equal( 1 );
  737. expectOperation( transOp[ 0 ], expected );
  738. } );
  739. it( 'target inside node from move range: no operation update', () => {
  740. const transformBy = new InsertOperation(
  741. new Position( root, [ 2, 2, 4, 1 ] ),
  742. [ nodeA, nodeB ],
  743. 0
  744. );
  745. const transOp = transform.transform( op, transformBy );
  746. expect( transOp.length ).to.equal( 1 );
  747. expectOperation( transOp[ 0 ], expected );
  748. } );
  749. it( 'target at offset before range offset: increment range offset', () => {
  750. const transformBy = new InsertOperation(
  751. new Position( root, [ 2, 2, 0 ] ),
  752. [ nodeA, nodeB ],
  753. 0
  754. );
  755. const transOp = transform.transform( op, transformBy );
  756. expected.sourcePosition.offset += 2;
  757. expect( transOp.length ).to.equal( 1 );
  758. expectOperation( transOp[ 0 ], expected );
  759. } );
  760. it( 'target at offset after range offset: no operation update', () => {
  761. const transformBy = new InsertOperation(
  762. new Position( root, [ 2, 2, 7 ] ),
  763. [ nodeA, nodeB ],
  764. 0
  765. );
  766. const transOp = transform.transform( op, transformBy );
  767. expect( transOp.length ).to.equal( 1 );
  768. expectOperation( transOp[ 0 ], expected );
  769. } );
  770. it( 'target before node from range start path: increment index on range start path', () => {
  771. const transformBy = new InsertOperation(
  772. new Position( root, [ 2, 0 ] ),
  773. [ nodeA, nodeB ],
  774. 0
  775. );
  776. const transOp = transform.transform( op, transformBy );
  777. expected.sourcePosition.path[ 1 ] += 2;
  778. expect( transOp.length ).to.equal( 1 );
  779. expectOperation( transOp[ 0 ], expected );
  780. } );
  781. it( 'target after node from range start path: no operation update', () => {
  782. const transformBy = new InsertOperation(
  783. new Position( root, [ 2, 3 ] ),
  784. [ nodeA, nodeB ],
  785. 0
  786. );
  787. const transOp = transform.transform( op, transformBy );
  788. expect( transOp.length ).to.equal( 1 );
  789. expectOperation( transOp[ 0 ], expected );
  790. } );
  791. it( 'target offset before move target offset: increment target offset', () => {
  792. const transformBy = new InsertOperation(
  793. new Position( root, [ 3, 3, 2 ] ),
  794. [ nodeA, nodeB ],
  795. 0
  796. );
  797. const transOp = transform.transform( op, transformBy );
  798. expected.targetPosition.offset += 2;
  799. expect( transOp.length ).to.equal( 1 );
  800. expectOperation( transOp[ 0 ], expected );
  801. } );
  802. it( 'target offset after move target offset: no operation update', () => {
  803. const transformBy = new InsertOperation(
  804. new Position( root, [ 3, 3, 4 ] ),
  805. [ nodeA, nodeB ],
  806. 0
  807. );
  808. const transOp = transform.transform( op, transformBy );
  809. expect( transOp.length ).to.equal( 1 );
  810. expectOperation( transOp[ 0 ], expected );
  811. } );
  812. it( 'target before node from move target position path: increment index on move target position path', () => {
  813. const transformBy = new InsertOperation(
  814. new Position( root, [ 3, 3 ] ),
  815. [ nodeA, nodeB ],
  816. 0
  817. );
  818. const transOp = transform.transform( op, transformBy );
  819. expected.targetPosition.path[ 1 ] += 2;
  820. expect( transOp.length ).to.equal( 1 );
  821. expectOperation( transOp[ 0 ], expected );
  822. } );
  823. it( 'target after node from move target position path: no operation update', () => {
  824. const transformBy = new InsertOperation(
  825. new Position( root, [ 3, 6 ] ),
  826. [ nodeA, nodeB ],
  827. 0
  828. );
  829. const transOp = transform.transform( op, transformBy );
  830. expect( transOp.length ).to.equal( 1 );
  831. expectOperation( transOp[ 0 ], expected );
  832. } );
  833. it( 'target offset same as move target offset and is important: no operation update', () => {
  834. const transformBy = new InsertOperation(
  835. new Position( root, [ 3, 3, 3 ] ),
  836. [ nodeA, nodeB ],
  837. 0
  838. );
  839. const transOp = transform.transform( op, transformBy );
  840. expect( transOp.length ).to.equal( 1 );
  841. expectOperation( transOp[ 0 ], expected );
  842. } );
  843. it( 'target offset same as move target offset and is less important: no operation update', () => {
  844. const transformBy = new InsertOperation(
  845. new Position( root, [ 3, 3, 3 ] ),
  846. [ nodeA, nodeB ],
  847. 0
  848. );
  849. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  850. expect( transOp.length ).to.equal( 1 );
  851. expectOperation( transOp[ 0 ], expected );
  852. } );
  853. it( 'target inside move range: expand range', () => {
  854. const transformBy = new InsertOperation(
  855. new Position( root, [ 2, 2, 5 ] ),
  856. [ nodeA, nodeB ],
  857. 0
  858. );
  859. const transOp = transform.transform( op, transformBy );
  860. expect( transOp.length ).to.equal( 1 );
  861. expected.howMany = 4;
  862. expectOperation( transOp[ 0 ], expected );
  863. } );
  864. it( 'target at offset same as range end boundary: no operation update', () => {
  865. const transformBy = new InsertOperation(
  866. new Position( root, [ 2, 2, 6 ] ),
  867. [ nodeA, nodeB ],
  868. 0
  869. );
  870. const transOp = transform.transform( op, transformBy );
  871. expect( transOp.length ).to.equal( 1 );
  872. expectOperation( transOp[ 0 ], expected );
  873. } );
  874. } );
  875. describe( 'by AttributeOperation', () => {
  876. it( 'no operation update', () => {
  877. const transformBy = new AttributeOperation(
  878. new Range( sourcePosition, rangeEnd ),
  879. 'abc',
  880. true,
  881. false,
  882. 0
  883. );
  884. const transOp = transform.transform( op, transformBy );
  885. expect( transOp.length ).to.equal( 1 );
  886. expectOperation( transOp[ 0 ], expected );
  887. } );
  888. } );
  889. describe( 'by RootAttributeOperation', () => {
  890. it( 'no operation update', () => {
  891. const transformBy = new RootAttributeOperation(
  892. root,
  893. 'foo',
  894. null,
  895. 'bar',
  896. 0
  897. );
  898. const transOp = transform.transform( op, transformBy );
  899. expect( transOp.length ).to.equal( 1 );
  900. expectOperation( transOp[ 0 ], expected );
  901. } );
  902. } );
  903. describe( 'by MoveOperation', () => {
  904. it( 'range and target different than transforming range and target: no operation update', () => {
  905. const transformBy = new MoveOperation(
  906. new Position( root, [ 1, 2 ] ),
  907. 3,
  908. new Position( root, [ 4, 1, 0 ] ),
  909. 0
  910. );
  911. const transOp = transform.transform( op, transformBy );
  912. expect( transOp.length ).to.equal( 1 );
  913. expectOperation( transOp[ 0 ], expected );
  914. } );
  915. it( 'target offset before transforming range start offset: increment range offset', () => {
  916. const transformBy = new MoveOperation(
  917. new Position( root, [ 4, 1, 0 ] ),
  918. 2,
  919. new Position( root, [ 2, 2, 0 ] ),
  920. 0
  921. );
  922. const transOp = transform.transform( op, transformBy );
  923. expected.sourcePosition.offset += 2;
  924. expect( transOp.length ).to.equal( 1 );
  925. expectOperation( transOp[ 0 ], expected );
  926. } );
  927. it( 'target offset after transforming range start offset: no operation update', () => {
  928. const transformBy = new MoveOperation(
  929. new Position( root, [ 4, 1, 0 ] ),
  930. 2,
  931. new Position( root, [ 2, 2, 7 ] ),
  932. 0
  933. );
  934. const transOp = transform.transform( op, transformBy );
  935. expect( transOp.length ).to.equal( 1 );
  936. expectOperation( transOp[ 0 ], expected );
  937. } );
  938. it( 'range start offset before transforming range start offset: decrement range offset', () => {
  939. const transformBy = new MoveOperation(
  940. new Position( root, [ 2, 2, 0 ] ),
  941. 2,
  942. new Position( root, [ 4, 1, 0 ] ),
  943. 0
  944. );
  945. const transOp = transform.transform( op, transformBy );
  946. expected.sourcePosition.offset -= 2;
  947. expect( transOp.length ).to.equal( 1 );
  948. expectOperation( transOp[ 0 ], expected );
  949. } );
  950. it( 'range start offset after transforming range start offset: no operation update', () => {
  951. const transformBy = new MoveOperation(
  952. new Position( root, [ 2, 2, 9 ] ),
  953. 2,
  954. new Position( root, [ 4, 1, 0 ] ),
  955. 0
  956. );
  957. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  958. expect( transOp.length ).to.equal( 1 );
  959. expectOperation( transOp[ 0 ], expected );
  960. } );
  961. it( 'target before node from transforming range start path: increment index on range start path', () => {
  962. const transformBy = new MoveOperation(
  963. new Position( root, [ 4, 1, 0 ] ),
  964. 2,
  965. new Position( root, [ 2, 1 ] ),
  966. 0
  967. );
  968. const transOp = transform.transform( op, transformBy );
  969. expected.sourcePosition.path[ 1 ] += 2;
  970. expect( transOp.length ).to.equal( 1 );
  971. expectOperation( transOp[ 0 ], expected );
  972. } );
  973. it( 'target after node from transforming range start path: no operation update', () => {
  974. const transformBy = new MoveOperation(
  975. new Position( root, [ 4, 1, 0 ] ),
  976. 2,
  977. new Position( root, [ 2, 5 ] ),
  978. 0
  979. );
  980. const transOp = transform.transform( op, transformBy );
  981. expect( transOp.length ).to.equal( 1 );
  982. expectOperation( transOp[ 0 ], expected );
  983. } );
  984. it( 'range before node from transforming range start path: decrement index on range start path', () => {
  985. const transformBy = new MoveOperation(
  986. new Position( root, [ 2, 0 ] ),
  987. 1,
  988. new Position( root, [ 4, 1, 0 ] ),
  989. 0
  990. );
  991. const transOp = transform.transform( op, transformBy );
  992. expected.sourcePosition.path[ 1 ] -= 1;
  993. expect( transOp.length ).to.equal( 1 );
  994. expectOperation( transOp[ 0 ], expected );
  995. } );
  996. it( 'range after node from transforming range start path: no operation update', () => {
  997. const transformBy = new MoveOperation(
  998. new Position( root, [ 2, 3 ] ),
  999. 2,
  1000. new Position( root, [ 4, 1, 0 ] ),
  1001. 0
  1002. );
  1003. const transOp = transform.transform( op, transformBy );
  1004. expect( transOp.length ).to.equal( 1 );
  1005. expectOperation( transOp[ 0 ], expected );
  1006. } );
  1007. it( 'target offset before transforming target offset: increment target offset', () => {
  1008. const transformBy = new MoveOperation(
  1009. new Position( root, [ 4, 1, 0 ] ),
  1010. 2,
  1011. new Position( root, [ 3, 3, 0 ] ),
  1012. 0
  1013. );
  1014. const transOp = transform.transform( op, transformBy );
  1015. expected.targetPosition.offset += 2;
  1016. expect( transOp.length ).to.equal( 1 );
  1017. expectOperation( transOp[ 0 ], expected );
  1018. } );
  1019. it( 'target offset after transforming target offset: no operation update', () => {
  1020. const transformBy = new MoveOperation(
  1021. new Position( root, [ 4, 1, 0 ] ),
  1022. 2,
  1023. new Position( root, [ 3, 3, 5 ] ),
  1024. 0
  1025. );
  1026. const transOp = transform.transform( op, transformBy );
  1027. expect( transOp.length ).to.equal( 1 );
  1028. expectOperation( transOp[ 0 ], expected );
  1029. } );
  1030. it( 'range offset before transforming target offset: decrement target offset', () => {
  1031. const transformBy = new MoveOperation(
  1032. new Position( root, [ 3, 3, 0 ] ),
  1033. 2,
  1034. new Position( root, [ 4, 1, 0 ] ),
  1035. 0
  1036. );
  1037. const transOp = transform.transform( op, transformBy );
  1038. expected.targetPosition.offset -= 2;
  1039. expect( transOp.length ).to.equal( 1 );
  1040. expectOperation( transOp[ 0 ], expected );
  1041. } );
  1042. it( 'range offset after transforming target offset: no operation update', () => {
  1043. const transformBy = new MoveOperation(
  1044. new Position( root, [ 3, 3, 5 ] ),
  1045. 2,
  1046. new Position( root, [ 4, 1, 0 ] ),
  1047. 0
  1048. );
  1049. const transOp = transform.transform( op, transformBy );
  1050. expect( transOp.length ).to.equal( 1 );
  1051. expectOperation( transOp[ 0 ], expected );
  1052. } );
  1053. it( 'target before node from transforming target path: increment index on target path', () => {
  1054. const transformBy = new MoveOperation(
  1055. new Position( root, [ 4, 1, 0 ] ),
  1056. 2,
  1057. new Position( root, [ 3, 1 ] ),
  1058. 0
  1059. );
  1060. const transOp = transform.transform( op, transformBy );
  1061. expected.targetPosition.path[ 1 ] += 2;
  1062. expect( transOp.length ).to.equal( 1 );
  1063. expectOperation( transOp[ 0 ], expected );
  1064. } );
  1065. it( 'target after node from transforming target path: no operation update', () => {
  1066. const transformBy = new MoveOperation(
  1067. new Position( root, [ 4, 1, 0 ] ),
  1068. 2,
  1069. new Position( root, [ 3, 5 ] ),
  1070. 0
  1071. );
  1072. const transOp = transform.transform( op, transformBy );
  1073. expect( transOp.length ).to.equal( 1 );
  1074. expectOperation( transOp[ 0 ], expected );
  1075. } );
  1076. it( 'range before node from transforming target path: decrement index on target path', () => {
  1077. const transformBy = new MoveOperation(
  1078. new Position( root, [ 3, 0 ] ),
  1079. 2,
  1080. new Position( root, [ 4, 1, 0 ] ),
  1081. 0
  1082. );
  1083. const transOp = transform.transform( op, transformBy );
  1084. expected.targetPosition.path[ 1 ] -= 2;
  1085. expect( transOp.length ).to.equal( 1 );
  1086. expectOperation( transOp[ 0 ], expected );
  1087. } );
  1088. it( 'range after node from transforming target path: no operation update', () => {
  1089. const transformBy = new MoveOperation(
  1090. new Position( root, [ 3, 5 ] ),
  1091. 2,
  1092. new Position( root, [ 4, 1, 0 ] ),
  1093. 0
  1094. );
  1095. const transOp = transform.transform( op, transformBy );
  1096. expect( transOp.length ).to.equal( 1 );
  1097. expectOperation( transOp[ 0 ], expected );
  1098. } );
  1099. it( 'target inside transforming move range: split into two operations', () => {
  1100. const transformBy = new MoveOperation(
  1101. new Position( root, [ 4, 1, 0 ] ),
  1102. 2,
  1103. new Position( root, [ 2, 2, 5 ] ),
  1104. 0
  1105. );
  1106. const transOp = transform.transform( op, transformBy );
  1107. expect( transOp.length ).to.equal( 2 );
  1108. expected.howMany = 1;
  1109. expectOperation( transOp[ 0 ], expected );
  1110. expected.sourcePosition.path = [ 2, 2, 6 ];
  1111. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1112. expectOperation( transOp[ 1 ], expected );
  1113. } );
  1114. it( 'target at start boundary of transforming move range: increment source offset', () => {
  1115. const transformBy = new MoveOperation(
  1116. new Position( root, [ 4, 1, 0 ] ),
  1117. 2,
  1118. new Position( root, [ 2, 2, 4 ] ),
  1119. 0
  1120. );
  1121. const transOp = transform.transform( op, transformBy );
  1122. expect( transOp.length ).to.equal( 1 );
  1123. expected.sourcePosition.offset = 6;
  1124. expectOperation( transOp[ 0 ], expected );
  1125. } );
  1126. it( 'target at end boundary of transforming move range: no operation update', () => {
  1127. const transformBy = new MoveOperation(
  1128. new Position( root, [ 4, 1, 0 ] ),
  1129. 2,
  1130. new Position( root, [ 2, 2, 6 ] ),
  1131. 0
  1132. );
  1133. const transOp = transform.transform( op, transformBy );
  1134. expect( transOp.length ).to.equal( 1 );
  1135. expectOperation( transOp[ 0 ], expected );
  1136. } );
  1137. it( 'target inside a node from transforming range: no operation update', () => {
  1138. const transformBy = new MoveOperation(
  1139. new Position( root, [ 4, 1, 0 ] ),
  1140. 2,
  1141. new Position( root, [ 2, 2, 5, 1 ] ),
  1142. 0
  1143. );
  1144. const transOp = transform.transform( op, transformBy );
  1145. expect( transOp.length ).to.equal( 1 );
  1146. expectOperation( transOp[ 0 ], expected );
  1147. } );
  1148. it( 'range has node that contains transforming range: update range path', () => {
  1149. const transformBy = new MoveOperation(
  1150. new Position( root, [ 2, 1 ] ),
  1151. 3,
  1152. new Position( root, [ 4, 2 ] ),
  1153. 0
  1154. );
  1155. const transOp = transform.transform( op, transformBy );
  1156. expected.sourcePosition.path = [ 4, 3, 4 ];
  1157. expect( transOp.length ).to.equal( 1 );
  1158. expectOperation( transOp[ 0 ], expected );
  1159. } );
  1160. it( 'range has node that contains transforming target: update target path', () => {
  1161. const transformBy = new MoveOperation(
  1162. new Position( root, [ 3, 2 ] ),
  1163. 3,
  1164. new Position( root, [ 0, 1 ] ),
  1165. 0
  1166. );
  1167. const transOp = transform.transform( op, transformBy );
  1168. expected.targetPosition.path = [ 0, 2, 3 ];
  1169. expect( transOp.length ).to.equal( 1 );
  1170. expectOperation( transOp[ 0 ], expected );
  1171. } );
  1172. it( 'target inside a node from transforming range and vice versa: reverse transform-by operation', () => {
  1173. const transformBy = new MoveOperation(
  1174. new Position( root, [ 3, 2 ] ),
  1175. 3,
  1176. new Position( root, [ 2, 2, 5, 0 ] ),
  1177. 0
  1178. );
  1179. const transOp = transform.transform( op, transformBy );
  1180. const reversed = transformBy.getReversed();
  1181. expected.sourcePosition = reversed.sourcePosition;
  1182. expected.targetPosition = reversed.targetPosition;
  1183. expected.howMany = reversed.howMany;
  1184. expect( transOp.length ).to.equal( 1 );
  1185. expectOperation( transOp[ 0 ], expected );
  1186. } );
  1187. it( 'range is same as transforming range and is important: convert to NoOperation', () => {
  1188. const transformBy = new MoveOperation(
  1189. op.sourcePosition,
  1190. op.howMany,
  1191. new Position( root, [ 4, 1, 0 ] ),
  1192. 0
  1193. );
  1194. const transOp = transform.transform( op, transformBy );
  1195. expect( transOp.length ).to.equal( 1 );
  1196. expectOperation( transOp[ 0 ], {
  1197. type: NoOperation
  1198. } );
  1199. } );
  1200. it( 'range is same as transforming range and is less important: update range path', () => {
  1201. const transformBy = new MoveOperation(
  1202. op.sourcePosition,
  1203. op.howMany,
  1204. new Position( root, [ 4, 1, 0 ] ),
  1205. 0
  1206. );
  1207. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1208. expected.sourcePosition.path = [ 4, 1, 0 ];
  1209. expect( transOp.length ).to.equal( 1 );
  1210. expectOperation( transOp[ 0 ], expected );
  1211. } );
  1212. it( 'range contains transforming range and is important: convert to NoOperation', () => {
  1213. const transformBy = new MoveOperation(
  1214. new Position( root, [ 2, 2, 3 ] ),
  1215. 4,
  1216. new Position( root, [ 4, 1, 0 ] ),
  1217. 0
  1218. );
  1219. const transOp = transform.transform( op, transformBy );
  1220. expect( transOp.length ).to.equal( 1 );
  1221. expectOperation( transOp[ 0 ], {
  1222. type: NoOperation
  1223. } );
  1224. } );
  1225. it( 'range contains transforming range and is less important: update range path', () => {
  1226. const transformBy = new MoveOperation(
  1227. new Position( root, [ 2, 2, 3 ] ),
  1228. 4,
  1229. new Position( root, [ 4, 1, 0 ] ),
  1230. 0
  1231. );
  1232. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1233. expected.sourcePosition.path = [ 4, 1, 1 ];
  1234. expect( transOp.length ).to.equal( 1 );
  1235. expectOperation( transOp[ 0 ], expected );
  1236. } );
  1237. it( 'range contains transforming range and target and is important: update range path and target', () => {
  1238. op.targetPosition.path = [ 2, 2, 7 ];
  1239. const transformBy = new MoveOperation(
  1240. new Position( root, [ 2, 2, 3 ] ),
  1241. 5,
  1242. new Position( root, [ 4, 1, 0 ] ),
  1243. 0
  1244. );
  1245. const transOp = transform.transform( op, transformBy );
  1246. expect( transOp.length ).to.equal( 1 );
  1247. expected.sourcePosition.path = [ 4, 1, 1 ];
  1248. expected.targetPosition.path = [ 4, 1, 4 ];
  1249. expectOperation( transOp[ 0 ], expected );
  1250. } );
  1251. it( 'range contains transforming range and target and is less important: update range path and target', () => {
  1252. op.targetPosition.path = [ 2, 2, 7 ];
  1253. const transformBy = new MoveOperation(
  1254. new Position( root, [ 2, 2, 3 ] ),
  1255. 5,
  1256. new Position( root, [ 4, 1, 0 ] ),
  1257. 0
  1258. );
  1259. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1260. expect( transOp.length ).to.equal( 1 );
  1261. expected.sourcePosition.path = [ 4, 1, 1 ];
  1262. expected.targetPosition.path = [ 4, 1, 4 ];
  1263. expectOperation( transOp[ 0 ], expected );
  1264. } );
  1265. it( 'range intersects on left side of transforming range and is important: shrink range', () => {
  1266. const transformBy = new MoveOperation(
  1267. new Position( root, [ 2, 2, 3 ] ),
  1268. 2,
  1269. new Position( root, [ 4, 1, 0 ] ),
  1270. 0
  1271. );
  1272. const transOp = transform.transform( op, transformBy );
  1273. expected.sourcePosition.path = [ 2, 2, 3 ];
  1274. expected.howMany = 1;
  1275. expect( transOp.length ).to.equal( 1 );
  1276. expectOperation( transOp[ 0 ], expected );
  1277. } );
  1278. it( 'range intersects on left side of transforming range and is less important: split into two operations', () => {
  1279. // Get more test cases and better code coverage
  1280. const otherRoot = new RootElement( null );
  1281. const transformBy = new MoveOperation(
  1282. new Position( root, [ 2, 2, 3 ] ),
  1283. 2,
  1284. new Position( otherRoot, [ 4, 1, 0 ] ),
  1285. 0
  1286. );
  1287. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1288. expect( transOp.length ).to.equal( 2 );
  1289. expected.sourcePosition = new Position( otherRoot, [ 4, 1, 1 ] );
  1290. expected.howMany = 1;
  1291. expectOperation( transOp[ 0 ], expected );
  1292. expected.sourcePosition = new Position( root, [ 2, 2, 3 ] );
  1293. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1294. expectOperation( transOp[ 1 ], expected );
  1295. } );
  1296. it( 'range intersects on right side of transforming range and is important: shrink range', () => {
  1297. const transformBy = new MoveOperation(
  1298. new Position( root, [ 2, 2, 5 ] ),
  1299. 2,
  1300. new Position( root, [ 4, 1, 0 ] ),
  1301. 0
  1302. );
  1303. const transOp = transform.transform( op, transformBy );
  1304. expected.howMany = 1;
  1305. expect( transOp.length ).to.equal( 1 );
  1306. expectOperation( transOp[ 0 ], expected );
  1307. } );
  1308. it( 'range intersects on right side of transforming range and is less important: split into two operations', () => {
  1309. const transformBy = new MoveOperation(
  1310. new Position( root, [ 2, 2, 5 ] ),
  1311. 2,
  1312. new Position( root, [ 4, 1, 0 ] ),
  1313. 0
  1314. );
  1315. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1316. expect( transOp.length ).to.equal( 2 );
  1317. expected.sourcePosition = sourcePosition;
  1318. expected.howMany = 1;
  1319. expectOperation( transOp[ 0 ], expected );
  1320. expected.sourcePosition = new Position( root, [ 4, 1, 0 ] );
  1321. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1322. expectOperation( transOp[ 1 ], expected );
  1323. } );
  1324. it( 'range intersects on left side, target inside transforming range and is important: split into two operations', () => {
  1325. op.howMany = 4;
  1326. const transformBy = new MoveOperation(
  1327. new Position( root, [ 2, 2, 3 ] ),
  1328. 2,
  1329. new Position( root, [ 2, 2, 6 ] ),
  1330. 0
  1331. );
  1332. const transOp = transform.transform( op, transformBy );
  1333. expect( transOp.length ).to.equal( 2 );
  1334. expected.sourcePosition.path = [ 2, 2, 3 ];
  1335. expected.howMany = 1;
  1336. expectOperation( transOp[ 0 ], expected );
  1337. expected.sourcePosition.path = [ 2, 2, 5 ];
  1338. expected.howMany = 2;
  1339. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1340. expectOperation( transOp[ 1 ], expected );
  1341. } );
  1342. it( 'range intersects on left side, target inside transforming range and is less important: split into two operations', () => {
  1343. op.howMany = 4;
  1344. const transformBy = new MoveOperation(
  1345. new Position( root, [ 2, 2, 3 ] ),
  1346. 2,
  1347. new Position( root, [ 2, 2, 6 ] ),
  1348. 0
  1349. );
  1350. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1351. expect( transOp.length ).to.equal( 2 );
  1352. expected.sourcePosition.path = [ 2, 2, 3 ];
  1353. expected.howMany = 1;
  1354. expectOperation( transOp[ 0 ], expected );
  1355. expected.sourcePosition.path = [ 2, 2, 5 ];
  1356. expected.howMany = 2;
  1357. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1358. expectOperation( transOp[ 1 ], expected );
  1359. } );
  1360. it( 'range intersects on right side of transforming range and is important: shrink range', () => {
  1361. const transformBy = new MoveOperation(
  1362. new Position( root, [ 2, 2, 5 ] ),
  1363. 2,
  1364. new Position( root, [ 4, 1, 0 ] ),
  1365. 0
  1366. );
  1367. const transOp = transform.transform( op, transformBy );
  1368. expected.howMany = 1;
  1369. expect( transOp.length ).to.equal( 1 );
  1370. expectOperation( transOp[ 0 ], expected );
  1371. } );
  1372. it( 'range intersects on right side of transforming range and is less important: split into two operations', () => {
  1373. const transformBy = new MoveOperation(
  1374. new Position( root, [ 2, 2, 5 ] ),
  1375. 2,
  1376. new Position( root, [ 4, 1, 0 ] ),
  1377. 0
  1378. );
  1379. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1380. expect( transOp.length ).to.equal( 2 );
  1381. expected.sourcePosition = sourcePosition;
  1382. expected.howMany = 1;
  1383. expectOperation( transOp[ 0 ], expected );
  1384. expected.sourcePosition = new Position( root, [ 4, 1, 0 ] );
  1385. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1386. expectOperation( transOp[ 1 ], expected );
  1387. } );
  1388. it( 'range intersects, target inside transforming range and is important: split into two operations', () => {
  1389. op.targetPosition.path = [ 2, 2, 7 ];
  1390. const transformBy = new MoveOperation(
  1391. new Position( root, [ 2, 2, 5 ] ),
  1392. 4,
  1393. new Position( root, [ 4, 1, 0 ] ),
  1394. 0
  1395. );
  1396. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1397. expect( transOp.length ).to.equal( 2 );
  1398. expected.sourcePosition.path = [ 2, 2, 4 ];
  1399. expected.targetPosition.path = [ 4, 1, 2 ];
  1400. expected.howMany = 1;
  1401. expectOperation( transOp[ 0 ], expected );
  1402. expected.sourcePosition.path = [ 4, 1, 0 ];
  1403. expected.targetPosition.path = [ 4, 1, 3 ];
  1404. expectOperation( transOp[ 1 ], expected );
  1405. } );
  1406. it( 'range intersects, target inside transforming range and is less important: shrink range', () => {
  1407. op.targetPosition.path = [ 2, 2, 7 ];
  1408. const transformBy = new MoveOperation(
  1409. new Position( root, [ 2, 2, 5 ] ),
  1410. 4,
  1411. new Position( root, [ 4, 1, 0 ] ),
  1412. 0
  1413. );
  1414. const transOp = transform.transform( op, transformBy );
  1415. expect( transOp.length ).to.equal( 1 );
  1416. expected.sourcePosition.path = [ 2, 2, 4 ];
  1417. expected.targetPosition.path = [ 4, 1, 2 ];
  1418. expected.howMany = 1;
  1419. expectOperation( transOp[ 0 ], expected );
  1420. } );
  1421. it( 'range inside transforming range and is important: split into two operations', () => {
  1422. op.howMany = 4;
  1423. const transformBy = new MoveOperation(
  1424. new Position( root, [ 2, 2, 5 ] ),
  1425. 2,
  1426. new Position( root, [ 4, 1, 0 ] ),
  1427. 0
  1428. );
  1429. const transOp = transform.transform( op, transformBy );
  1430. expect( transOp.length ).to.equal( 2 );
  1431. expected.sourcePosition.path = [ 2, 2, 4 ];
  1432. expected.howMany = 1;
  1433. expectOperation( transOp[ 0 ], expected );
  1434. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1435. expectOperation( transOp[ 1 ], expected );
  1436. } );
  1437. it( 'range inside transforming range and is less important: split into three operations', () => {
  1438. op.howMany = 4;
  1439. const transformBy = new MoveOperation(
  1440. new Position( root, [ 2, 2, 5 ] ),
  1441. 2,
  1442. new Position( root, [ 4, 1, 0 ] ),
  1443. 0
  1444. );
  1445. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1446. expect( transOp.length ).to.equal( 3 );
  1447. expected.sourcePosition.path = [ 2, 2, 4 ];
  1448. expected.howMany = 1;
  1449. expectOperation( transOp[ 0 ], expected );
  1450. expected.sourcePosition.path = [ 4, 1, 0 ];
  1451. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1452. expected.howMany = 2;
  1453. expectOperation( transOp[ 1 ], expected );
  1454. expected.sourcePosition.path = [ 2, 2, 4 ];
  1455. expected.targetPosition = targetPosition.getShiftedBy( 3 );
  1456. expected.howMany = 1;
  1457. expectOperation( transOp[ 2 ], expected );
  1458. } );
  1459. it( 'range and target inside transforming range and is important: no operation update', () => {
  1460. op.howMany = 6;
  1461. const transformBy = new MoveOperation(
  1462. new Position( root, [ 2, 2, 5 ] ),
  1463. 2,
  1464. new Position( root, [ 2, 2, 9 ] ),
  1465. 0
  1466. );
  1467. const transOp = transform.transform( op, transformBy );
  1468. expect( transOp.length ).to.equal( 1 );
  1469. expected.howMany = 6;
  1470. expectOperation( transOp[ 0 ], expected );
  1471. } );
  1472. it( 'range and target inside transforming range and is less important: no operation update', () => {
  1473. op.howMany = 6;
  1474. const transformBy = new MoveOperation(
  1475. new Position( root, [ 2, 2, 5 ] ),
  1476. 2,
  1477. new Position( root, [ 2, 2, 9 ] ),
  1478. 0
  1479. );
  1480. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1481. expect( transOp.length ).to.equal( 1 );
  1482. expected.howMany = 6;
  1483. expectOperation( transOp[ 0 ], expected );
  1484. } );
  1485. } );
  1486. describe( 'by MoveOperation to graveyard', () => {
  1487. let transformBy;
  1488. beforeEach( () => {
  1489. transformBy = new MoveOperation(
  1490. Position.createFromPosition( op.sourcePosition ),
  1491. op.howMany,
  1492. new Position( doc.graveyard, [ 0 ] ),
  1493. 0
  1494. );
  1495. } );
  1496. it( 'should skip context.aIsStrong and be less important than MoveOperation', () => {
  1497. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1498. expect( transOp.length ).to.equal( 1 );
  1499. expectOperation( transOp[ 0 ], {
  1500. type: NoOperation
  1501. } );
  1502. } );
  1503. } );
  1504. describe( 'by NoOperation', () => {
  1505. it( 'no operation update', () => {
  1506. const transformBy = new NoOperation( 0 );
  1507. const transOp = transform.transform( op, transformBy );
  1508. expect( transOp.length ).to.equal( 1 );
  1509. expectOperation( transOp[ 0 ], expected );
  1510. } );
  1511. } );
  1512. describe( 'by RenameOperation', () => {
  1513. it( 'no position update', () => {
  1514. const transformBy = new RenameOperation( new Position( root, [ 2, 2, 4 ] ), 'oldName', 'newName', 0 );
  1515. const transOp = transform.transform( op, transformBy );
  1516. expect( transOp.length ).to.equal( 1 );
  1517. expectOperation( transOp[ 0 ], expected );
  1518. } );
  1519. } );
  1520. describe( 'by MarkerOperation', () => {
  1521. it( 'no position update', () => {
  1522. const newRange = new Range( new Position( root, [ 2, 2, 3 ] ), new Position( root, [ 2, 2, 8 ] ) );
  1523. const transformBy = new MarkerOperation( 'name', null, newRange, model.markers, 0 );
  1524. const transOp = transform.transform( op, transformBy );
  1525. expect( transOp.length ).to.equal( 1 );
  1526. expectOperation( transOp[ 0 ], expected );
  1527. } );
  1528. } );
  1529. } );
  1530. describe( 'MoveOperation to graveyard', () => {
  1531. describe( 'by MoveOperation', () => {
  1532. it( 'should force removing content even if was less important', () => {
  1533. const op = new MoveOperation( new Position( root, [ 8 ] ), 2, new Position( doc.graveyard, [ 0 ] ), 0 );
  1534. const targetPosition = Position.createFromPosition( op.targetPosition );
  1535. const transformBy = new MoveOperation( new Position( root, [ 8 ] ), 2, new Position( root, [ 1 ] ), 0 );
  1536. const sourcePosition = Position.createFromPosition( transformBy.targetPosition );
  1537. const transOp = transform.transform( op, transformBy );
  1538. expect( transOp.length ).to.equal( 1 );
  1539. expectOperation( transOp[ 0 ], {
  1540. type: MoveOperation,
  1541. howMany: 2,
  1542. sourcePosition,
  1543. targetPosition
  1544. } );
  1545. } );
  1546. } );
  1547. } );
  1548. describe( 'NoOperation', () => {
  1549. beforeEach( () => {
  1550. op = new NoOperation( 0 );
  1551. expected = {
  1552. type: NoOperation
  1553. };
  1554. } );
  1555. describe( 'by InsertOperation', () => {
  1556. it( 'no operation update', () => {
  1557. const transformBy = new InsertOperation(
  1558. new Position( root, [ 0 ] ),
  1559. 'a',
  1560. 0
  1561. );
  1562. const transOp = transform.transform( op, transformBy );
  1563. expect( transOp.length ).to.equal( 1 );
  1564. expectOperation( transOp[ 0 ], expected );
  1565. } );
  1566. } );
  1567. describe( 'by AttributeOperation', () => {
  1568. it( 'no operation update', () => {
  1569. const transformBy = new AttributeOperation(
  1570. new Range(
  1571. new Position( root, [ 0 ] ),
  1572. new Position( root, [ 1 ] )
  1573. ),
  1574. 'foo',
  1575. 'bar',
  1576. 'xyz',
  1577. 0
  1578. );
  1579. const transOp = transform.transform( op, transformBy );
  1580. expect( transOp.length ).to.equal( 1 );
  1581. expectOperation( transOp[ 0 ], expected );
  1582. } );
  1583. } );
  1584. describe( 'by RootAttributeOperation', () => {
  1585. it( 'no operation update', () => {
  1586. const transformBy = new RootAttributeOperation(
  1587. root,
  1588. 'foo',
  1589. null,
  1590. 'bar',
  1591. 0
  1592. );
  1593. const transOp = transform.transform( op, transformBy );
  1594. expect( transOp.length ).to.equal( 1 );
  1595. expectOperation( transOp[ 0 ], expected );
  1596. } );
  1597. } );
  1598. describe( 'by MoveOperation', () => {
  1599. it( 'no operation update', () => {
  1600. const transformBy = new MoveOperation(
  1601. new Position( root, [ 0 ] ),
  1602. 2,
  1603. new Position( root, [ 1 ] ),
  1604. 0
  1605. );
  1606. const transOp = transform.transform( op, transformBy );
  1607. expect( transOp.length ).to.equal( 1 );
  1608. expectOperation( transOp[ 0 ], expected );
  1609. } );
  1610. } );
  1611. describe( 'by NoOperation', () => {
  1612. it( 'no operation update', () => {
  1613. const transformBy = new NoOperation( 0 );
  1614. const transOp = transform.transform( op, transformBy );
  1615. expect( transOp.length ).to.equal( 1 );
  1616. expectOperation( transOp[ 0 ], expected );
  1617. } );
  1618. } );
  1619. describe( 'by RenameOperation', () => {
  1620. it( 'no position update', () => {
  1621. const transformBy = new RenameOperation( new Position( root, [ 0, 2, 0 ] ), 'oldName', 'newName', 0 );
  1622. const transOp = transform.transform( op, transformBy );
  1623. expect( transOp.length ).to.equal( 1 );
  1624. expectOperation( transOp[ 0 ], expected );
  1625. } );
  1626. } );
  1627. describe( 'by MarkerOperation', () => {
  1628. it( 'no position update', () => {
  1629. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 8 ] ) );
  1630. const transformBy = new MarkerOperation( 'name', null, newRange, model.markers, 0 );
  1631. const transOp = transform.transform( op, transformBy );
  1632. expect( transOp.length ).to.equal( 1 );
  1633. expectOperation( transOp[ 0 ], expected );
  1634. } );
  1635. } );
  1636. } );
  1637. describe( 'RenameOperation', () => {
  1638. beforeEach( () => {
  1639. const position = new Position( root, [ 0, 2, 2 ] );
  1640. op = new RenameOperation( position, 'oldName', 'newName', 0 );
  1641. expected = {
  1642. position,
  1643. oldName: 'oldName',
  1644. newName: 'newName'
  1645. };
  1646. } );
  1647. describe( 'by InsertOperation', () => {
  1648. it( 'target before renamed element: offset update', () => {
  1649. const transformBy = new InsertOperation(
  1650. new Position( root, [ 0, 2, 1 ] ),
  1651. [ nodeA, nodeB ],
  1652. 0
  1653. );
  1654. const transOp = transform.transform( op, transformBy );
  1655. expect( transOp.length ).to.equal( 1 );
  1656. expected.position.offset = 4;
  1657. expectOperation( transOp[ 0 ], expected );
  1658. } );
  1659. it( 'target after renamed element: no change', () => {
  1660. const transformBy = new InsertOperation(
  1661. new Position( root, [ 0, 2, 3 ] ),
  1662. [ nodeA, nodeB ],
  1663. 0
  1664. );
  1665. const transOp = transform.transform( op, transformBy );
  1666. expect( transOp.length ).to.equal( 1 );
  1667. expectOperation( transOp[ 0 ], expected );
  1668. } );
  1669. it( 'target before a node on path to renamed element: path update', () => {
  1670. const transformBy = new InsertOperation(
  1671. new Position( root, [ 0, 1 ] ),
  1672. [ nodeA, nodeB ],
  1673. 0
  1674. );
  1675. const transOp = transform.transform( op, transformBy );
  1676. expect( transOp.length ).to.equal( 1 );
  1677. expected.position.path = [ 0, 4, 2 ];
  1678. expectOperation( transOp[ 0 ], expected );
  1679. } );
  1680. it( 'target after a node on path to renamed element: no change', () => {
  1681. const transformBy = new InsertOperation(
  1682. new Position( root, [ 0, 3 ] ),
  1683. [ nodeA, nodeB ],
  1684. 0
  1685. );
  1686. const transOp = transform.transform( op, transformBy );
  1687. expect( transOp.length ).to.equal( 1 );
  1688. expectOperation( transOp[ 0 ], expected );
  1689. } );
  1690. } );
  1691. describe( 'by AttributeOperation', () => {
  1692. it( 'no operation update', () => {
  1693. const transformBy = new AttributeOperation(
  1694. new Range(
  1695. new Position( root, [ 0, 2, 1 ] ),
  1696. new Position( root, [ 1, 3 ] )
  1697. ),
  1698. 'foo',
  1699. 'bar',
  1700. 'xyz',
  1701. 0
  1702. );
  1703. const transOp = transform.transform( op, transformBy );
  1704. expect( transOp.length ).to.equal( 1 );
  1705. expectOperation( transOp[ 0 ], expected );
  1706. } );
  1707. } );
  1708. describe( 'by RootAttributeOperation', () => {
  1709. it( 'no operation update', () => {
  1710. const transformBy = new RootAttributeOperation(
  1711. root,
  1712. 'foo',
  1713. null,
  1714. 'bar',
  1715. 0
  1716. );
  1717. const transOp = transform.transform( op, transformBy );
  1718. expect( transOp.length ).to.equal( 1 );
  1719. expectOperation( transOp[ 0 ], expected );
  1720. } );
  1721. } );
  1722. describe( 'by MarkerOperation', () => {
  1723. it( 'no operation update', () => {
  1724. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 8 ] ) );
  1725. const transformBy = new MarkerOperation( 'name', null, newRange, model.markers, 0 );
  1726. const transOp = transform.transform( op, transformBy );
  1727. expect( transOp.length ).to.equal( 1 );
  1728. expectOperation( transOp[ 0 ], expected );
  1729. } );
  1730. } );
  1731. describe( 'by RenameOperation', () => {
  1732. it( 'different element: no change', () => {
  1733. const transformBy = new RenameOperation(
  1734. new Position( root, [ 0, 2, 1 ] ),
  1735. 'foo',
  1736. 'bar',
  1737. 0
  1738. );
  1739. const transOp = transform.transform( op, transformBy );
  1740. expect( transOp.length ).to.equal( 1 );
  1741. expectOperation( transOp[ 0 ], expected );
  1742. } );
  1743. it( 'same element and is important: convert to NoOperation', () => {
  1744. const transformBy = new RenameOperation(
  1745. new Position( root, [ 0, 2, 2 ] ),
  1746. 'oldName',
  1747. 'otherName',
  1748. 0
  1749. );
  1750. const transOp = transform.transform( op, transformBy );
  1751. expect( transOp.length ).to.equal( 1 );
  1752. expectOperation( transOp[ 0 ], {
  1753. type: NoOperation
  1754. } );
  1755. } );
  1756. it( 'same element and is not important: change old name to new name', () => {
  1757. const transformBy = new RenameOperation(
  1758. new Position( root, [ 0, 2, 2 ] ),
  1759. 'oldName',
  1760. 'otherName',
  1761. 0
  1762. );
  1763. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1764. expect( transOp.length ).to.equal( 1 );
  1765. expected.oldName = 'otherName';
  1766. expectOperation( transOp[ 0 ], expected );
  1767. } );
  1768. } );
  1769. describe( 'by MoveOperation', () => {
  1770. it( 'moved range before renamed element: update offset', () => {
  1771. const transformBy = new MoveOperation(
  1772. new Position( root, [ 0, 2, 0 ] ),
  1773. 2,
  1774. new Position( root, [ 2, 5 ] ),
  1775. 0
  1776. );
  1777. const transOp = transform.transform( op, transformBy );
  1778. expect( transOp.length ).to.equal( 1 );
  1779. expected.position.offset = 0;
  1780. expectOperation( transOp[ 0 ], expected );
  1781. } );
  1782. it( 'moved range before an element on path to renamed element: update path', () => {
  1783. const transformBy = new MoveOperation(
  1784. new Position( root, [ 0, 0 ] ),
  1785. 2,
  1786. new Position( root, [ 2, 5 ] ),
  1787. 0
  1788. );
  1789. const transOp = transform.transform( op, transformBy );
  1790. expect( transOp.length ).to.equal( 1 );
  1791. expected.position.path = [ 0, 0, 2 ];
  1792. expectOperation( transOp[ 0 ], expected );
  1793. } );
  1794. it( 'moved range contains renamed element: update path', () => {
  1795. const transformBy = new MoveOperation(
  1796. new Position( root, [ 0, 2, 1 ] ),
  1797. 3,
  1798. new Position( root, [ 2, 5 ] ),
  1799. 0
  1800. );
  1801. const transOp = transform.transform( op, transformBy );
  1802. expect( transOp.length ).to.equal( 1 );
  1803. expected.position.path = [ 2, 6 ];
  1804. expectOperation( transOp[ 0 ], expected );
  1805. } );
  1806. it( 'moved range contains renamed element parent: updated path', () => {
  1807. const transformBy = new MoveOperation(
  1808. new Position( root, [ 0, 1 ] ),
  1809. 3,
  1810. new Position( root, [ 2, 5 ] ),
  1811. 0
  1812. );
  1813. const transOp = transform.transform( op, transformBy );
  1814. expect( transOp.length ).to.equal( 1 );
  1815. expected.position.path = [ 2, 6, 2 ];
  1816. expectOperation( transOp[ 0 ], expected );
  1817. } );
  1818. it( 'move target before renamed element: update offset', () => {
  1819. const transformBy = new MoveOperation(
  1820. new Position( root, [ 2, 5 ] ),
  1821. 2,
  1822. new Position( root, [ 0, 2, 1 ] ),
  1823. 0
  1824. );
  1825. const transOp = transform.transform( op, transformBy );
  1826. expect( transOp.length ).to.equal( 1 );
  1827. expected.position.offset = 4;
  1828. expectOperation( transOp[ 0 ], expected );
  1829. } );
  1830. it( 'move target before an element on path to renamed element: update path', () => {
  1831. const transformBy = new MoveOperation(
  1832. new Position( root, [ 0, 2, 0 ] ),
  1833. 2,
  1834. new Position( root, [ 2, 5 ] ),
  1835. 0
  1836. );
  1837. const transOp = transform.transform( op, transformBy );
  1838. expect( transOp.length ).to.equal( 1 );
  1839. expected.position.offset = 0;
  1840. expectOperation( transOp[ 0 ], expected );
  1841. } );
  1842. } );
  1843. } );
  1844. describe( 'MarkerOperation', () => {
  1845. let oldRange, newRange;
  1846. beforeEach( () => {
  1847. oldRange = Range.createFromParentsAndOffsets( root, 1, root, 4 );
  1848. newRange = Range.createFromParentsAndOffsets( root, 10, root, 12 );
  1849. op = new MarkerOperation( 'name', oldRange, newRange, model.markers, 0 );
  1850. expected = {
  1851. name: 'name',
  1852. oldRange,
  1853. newRange
  1854. };
  1855. } );
  1856. describe( 'by InsertOperation', () => {
  1857. it( 'insert position affecting oldRange: update oldRange', () => {
  1858. // Just CC things.
  1859. op.newRange = null;
  1860. const transformBy = new InsertOperation( Position.createAt( root, 0 ), [ nodeA, nodeB ], 0 );
  1861. const transOp = transform.transform( op, transformBy );
  1862. expected.newRange = null;
  1863. expected.oldRange.start.offset = 3;
  1864. expected.oldRange.end.offset = 6;
  1865. expect( transOp.length ).to.equal( 1 );
  1866. expectOperation( transOp[ 0 ], expected );
  1867. } );
  1868. it( 'insert position affecting newRange: update newRange', () => {
  1869. // Just CC things.
  1870. op.oldRange = null;
  1871. const transformBy = new InsertOperation( Position.createAt( root, 8 ), [ nodeA, nodeB ], 0 );
  1872. const transOp = transform.transform( op, transformBy );
  1873. expected.oldRange = null;
  1874. expected.newRange.start.offset = 12;
  1875. expected.newRange.end.offset = 14;
  1876. expect( transOp.length ).to.equal( 1 );
  1877. expectOperation( transOp[ 0 ], expected );
  1878. } );
  1879. } );
  1880. describe( 'by AttributeOperation', () => {
  1881. it( 'no operation update', () => {
  1882. const transformBy = new AttributeOperation(
  1883. new Range(
  1884. new Position( root, [ 2 ] ),
  1885. new Position( root, [ 11 ] )
  1886. ),
  1887. 'foo',
  1888. 'bar',
  1889. 'xyz',
  1890. 0
  1891. );
  1892. const transOp = transform.transform( op, transformBy );
  1893. expect( transOp.length ).to.equal( 1 );
  1894. expectOperation( transOp[ 0 ], expected );
  1895. } );
  1896. } );
  1897. describe( 'by MoveOperation', () => {
  1898. it( 'moved range is before oldRange: update oldRange', () => {
  1899. // Just CC things.
  1900. op.newRange = null;
  1901. const transformBy = new MoveOperation( Position.createAt( root, 0 ), 1, Position.createAt( root, 20 ), 0 );
  1902. const transOp = transform.transform( op, transformBy );
  1903. expected.newRange = null;
  1904. expected.oldRange.start.offset = 0;
  1905. expected.oldRange.end.offset = 3;
  1906. expect( transOp.length ).to.equal( 1 );
  1907. expectOperation( transOp[ 0 ], expected );
  1908. } );
  1909. it( 'moved range contains oldRange and is before newRange: update oldRange and newRange', () => {
  1910. const transformBy = new MoveOperation( Position.createAt( root, 2 ), 2, Position.createAt( root, 20 ), 0 );
  1911. const transOp = transform.transform( op, transformBy );
  1912. expected.oldRange.start.offset = 1;
  1913. expected.oldRange.end.offset = 2;
  1914. expected.newRange.start.offset = 8;
  1915. expected.newRange.end.offset = 10;
  1916. expect( transOp.length ).to.equal( 1 );
  1917. expectOperation( transOp[ 0 ], expected );
  1918. } );
  1919. it( 'target position is inside newRange: update newRange', () => {
  1920. // Just CC things.
  1921. op.oldRange = null;
  1922. const transformBy = new MoveOperation( Position.createAt( root, 20 ), 2, Position.createAt( root, 11 ), 0 );
  1923. const transOp = transform.transform( op, transformBy );
  1924. expected.oldRange = null;
  1925. expected.newRange.start.offset = 10;
  1926. expected.newRange.end.offset = 14;
  1927. expect( transOp.length ).to.equal( 1 );
  1928. expectOperation( transOp[ 0 ], expected );
  1929. } );
  1930. it( 'target position is inside oldRange and before newRange: update oldRange and newRange', () => {
  1931. const transformBy = new MoveOperation( Position.createAt( root, 20 ), 4, Position.createAt( root, 2 ), 0 );
  1932. const transOp = transform.transform( op, transformBy );
  1933. expected.oldRange.start.offset = 1;
  1934. expected.oldRange.end.offset = 8;
  1935. expected.newRange.start.offset = 14;
  1936. expected.newRange.end.offset = 16;
  1937. expect( transOp.length ).to.equal( 1 );
  1938. expectOperation( transOp[ 0 ], expected );
  1939. } );
  1940. } );
  1941. describe( 'by RootAttributeOperation', () => {
  1942. it( 'no operation update', () => {
  1943. const transformBy = new RootAttributeOperation(
  1944. root,
  1945. 'foo',
  1946. null,
  1947. 'bar',
  1948. 0
  1949. );
  1950. const transOp = transform.transform( op, transformBy );
  1951. expect( transOp.length ).to.equal( 1 );
  1952. expectOperation( transOp[ 0 ], expected );
  1953. } );
  1954. } );
  1955. describe( 'by RenameOperation', () => {
  1956. it( 'no operation update', () => {
  1957. const transformBy = new RenameOperation( new Position( root, [ 1 ] ), 'oldName', 'newName', 0 );
  1958. const transOp = transform.transform( op, transformBy );
  1959. expect( transOp.length ).to.equal( 1 );
  1960. expectOperation( transOp[ 0 ], expected );
  1961. } );
  1962. } );
  1963. describe( 'by MarkerOperation', () => {
  1964. it( 'different marker name: no operation update', () => {
  1965. const transformBy = new MarkerOperation( 'otherName', oldRange, newRange, model.markers, 0 );
  1966. const transOp = transform.transform( op, transformBy );
  1967. expect( transOp.length ).to.equal( 1 );
  1968. expectOperation( transOp[ 0 ], expected );
  1969. } );
  1970. it( 'same marker name and is important: convert to NoOperation', () => {
  1971. const anotherRange = Range.createFromParentsAndOffsets( root, 2, root, 2 );
  1972. const transformBy = new MarkerOperation( 'name', oldRange, anotherRange, model.markers, 0 );
  1973. const transOp = transform.transform( op, transformBy );
  1974. expect( transOp.length ).to.equal( 1 );
  1975. expectOperation( transOp[ 0 ], {
  1976. type: NoOperation
  1977. } );
  1978. } );
  1979. it( 'same marker name and is less important: update oldRange parameter', () => {
  1980. const anotherRange = Range.createFromParentsAndOffsets( root, 2, root, 2 );
  1981. const transformBy = new MarkerOperation( 'name', oldRange, anotherRange, model.markers, 0 );
  1982. const transOp = transform.transform( op, transformBy, { aIsStrong: true } );
  1983. expected.oldRange = anotherRange;
  1984. expect( transOp.length ).to.equal( 1 );
  1985. expectOperation( transOp[ 0 ], expected );
  1986. } );
  1987. } );
  1988. } );
  1989. } );