8
0

transform.js 68 KB

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