8
0

transform.js 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607
  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( 2 );
  1342. expected.sourcePosition.path = [ 2, 2, 3 ];
  1343. expected.howMany = 1;
  1344. expectOperation( transOp[ 0 ], expected );
  1345. expected.sourcePosition.path = [ 2, 2, 5 ];
  1346. expected.howMany = 2;
  1347. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1348. expectOperation( transOp[ 1 ], expected );
  1349. } );
  1350. it( 'range intersects on right side of transforming range and is important: shrink range', () => {
  1351. const transformBy = new MoveOperation(
  1352. new Position( root, [ 2, 2, 5 ] ),
  1353. 2,
  1354. new Position( root, [ 4, 1, 0 ] ),
  1355. 0
  1356. );
  1357. const transOp = transform( op, transformBy );
  1358. expected.howMany = 1;
  1359. expect( transOp.length ).to.equal( 1 );
  1360. expectOperation( transOp[ 0 ], expected );
  1361. } );
  1362. it( 'range intersects on right side of transforming range and is less important: split into two operations', () => {
  1363. const transformBy = new MoveOperation(
  1364. new Position( root, [ 2, 2, 5 ] ),
  1365. 2,
  1366. new Position( root, [ 4, 1, 0 ] ),
  1367. 0
  1368. );
  1369. const transOp = transform( op, transformBy, strongContext );
  1370. expect( transOp.length ).to.equal( 2 );
  1371. expected.sourcePosition = sourcePosition;
  1372. expected.howMany = 1;
  1373. expectOperation( transOp[ 0 ], expected );
  1374. expected.sourcePosition = new Position( root, [ 4, 1, 0 ] );
  1375. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1376. expectOperation( transOp[ 1 ], expected );
  1377. } );
  1378. it( 'range intersects, target inside transforming range and is important: split into two operations', () => {
  1379. op.targetPosition.path = [ 2, 2, 7 ];
  1380. const transformBy = new MoveOperation(
  1381. new Position( root, [ 2, 2, 5 ] ),
  1382. 4,
  1383. new Position( root, [ 4, 1, 0 ] ),
  1384. 0
  1385. );
  1386. const transOp = transform( op, transformBy, strongContext );
  1387. expect( transOp.length ).to.equal( 2 );
  1388. expected.sourcePosition.path = [ 2, 2, 4 ];
  1389. expected.targetPosition.path = [ 4, 1, 2 ];
  1390. expected.howMany = 1;
  1391. expectOperation( transOp[ 0 ], expected );
  1392. expected.sourcePosition.path = [ 4, 1, 0 ];
  1393. expected.targetPosition.path = [ 4, 1, 3 ];
  1394. expectOperation( transOp[ 1 ], expected );
  1395. } );
  1396. it( 'range intersects, target inside transforming range and is less important: shrink range', () => {
  1397. op.targetPosition.path = [ 2, 2, 7 ];
  1398. const transformBy = new MoveOperation(
  1399. new Position( root, [ 2, 2, 5 ] ),
  1400. 4,
  1401. new Position( root, [ 4, 1, 0 ] ),
  1402. 0
  1403. );
  1404. const transOp = transform( op, transformBy );
  1405. expect( transOp.length ).to.equal( 1 );
  1406. expected.sourcePosition.path = [ 2, 2, 4 ];
  1407. expected.targetPosition.path = [ 4, 1, 2 ];
  1408. expected.howMany = 1;
  1409. expectOperation( transOp[ 0 ], expected );
  1410. } );
  1411. it( 'range inside transforming range and is important: split into two operations', () => {
  1412. op.howMany = 4;
  1413. const transformBy = new MoveOperation(
  1414. new Position( root, [ 2, 2, 5 ] ),
  1415. 2,
  1416. new Position( root, [ 4, 1, 0 ] ),
  1417. 0
  1418. );
  1419. const transOp = transform( op, transformBy );
  1420. expect( transOp.length ).to.equal( 2 );
  1421. expected.sourcePosition.path = [ 2, 2, 4 ];
  1422. expected.howMany = 1;
  1423. expectOperation( transOp[ 0 ], expected );
  1424. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1425. expectOperation( transOp[ 1 ], expected );
  1426. } );
  1427. it( 'range inside transforming range and is less important: split into three operations', () => {
  1428. op.howMany = 4;
  1429. const transformBy = new MoveOperation(
  1430. new Position( root, [ 2, 2, 5 ] ),
  1431. 2,
  1432. new Position( root, [ 4, 1, 0 ] ),
  1433. 0
  1434. );
  1435. const transOp = transform( op, transformBy, strongContext );
  1436. expect( transOp.length ).to.equal( 3 );
  1437. expected.sourcePosition.path = [ 2, 2, 4 ];
  1438. expected.howMany = 1;
  1439. expectOperation( transOp[ 0 ], expected );
  1440. expected.sourcePosition.path = [ 4, 1, 0 ];
  1441. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1442. expected.howMany = 2;
  1443. expectOperation( transOp[ 1 ], expected );
  1444. expected.sourcePosition.path = [ 2, 2, 4 ];
  1445. expected.targetPosition = targetPosition.getShiftedBy( 3 );
  1446. expected.howMany = 1;
  1447. expectOperation( transOp[ 2 ], expected );
  1448. } );
  1449. it( 'range and target inside transforming range and is important: no operation update', () => {
  1450. op.howMany = 6;
  1451. const transformBy = new MoveOperation(
  1452. new Position( root, [ 2, 2, 5 ] ),
  1453. 2,
  1454. new Position( root, [ 2, 2, 9 ] ),
  1455. 0
  1456. );
  1457. const transOp = transform( op, transformBy );
  1458. expect( transOp.length ).to.equal( 1 );
  1459. expected.howMany = 6;
  1460. expectOperation( transOp[ 0 ], expected );
  1461. } );
  1462. it( 'range and target inside transforming range and is less important: no operation update', () => {
  1463. op.howMany = 6;
  1464. const transformBy = new MoveOperation(
  1465. new Position( root, [ 2, 2, 5 ] ),
  1466. 2,
  1467. new Position( root, [ 2, 2, 9 ] ),
  1468. 0
  1469. );
  1470. const transOp = transform( op, transformBy, strongContext );
  1471. expect( transOp.length ).to.equal( 1 );
  1472. expected.howMany = 6;
  1473. expectOperation( transOp[ 0 ], expected );
  1474. } );
  1475. } );
  1476. describe( 'by MoveOperation to graveyard', () => {
  1477. let transformBy;
  1478. beforeEach( () => {
  1479. transformBy = new MoveOperation(
  1480. Position.createFromPosition( op.sourcePosition ),
  1481. op.howMany,
  1482. new Position( doc.graveyard, [ 0 ] ),
  1483. 0
  1484. );
  1485. } );
  1486. it( 'should skip context.aIsStrong and be less important than MoveOperation', () => {
  1487. const transOp = transform( op, transformBy, strongContext );
  1488. expect( transOp.length ).to.equal( 1 );
  1489. expectOperation( transOp[ 0 ], {
  1490. type: NoOperation
  1491. } );
  1492. } );
  1493. } );
  1494. describe( 'by NoOperation', () => {
  1495. it( 'no operation update', () => {
  1496. const transformBy = new NoOperation( 0 );
  1497. const transOp = transform( op, transformBy );
  1498. expect( transOp.length ).to.equal( 1 );
  1499. expectOperation( transOp[ 0 ], expected );
  1500. } );
  1501. } );
  1502. describe( 'by RenameOperation', () => {
  1503. it( 'no position update', () => {
  1504. const transformBy = new RenameOperation( new Position( root, [ 2, 2, 4 ] ), 'oldName', 'newName', 0 );
  1505. const transOp = transform( op, transformBy );
  1506. expect( transOp.length ).to.equal( 1 );
  1507. expectOperation( transOp[ 0 ], expected );
  1508. } );
  1509. } );
  1510. describe( 'by MarkerOperation', () => {
  1511. it( 'no position update', () => {
  1512. const newRange = new Range( new Position( root, [ 2, 2, 3 ] ), new Position( root, [ 2, 2, 8 ] ) );
  1513. const transformBy = new MarkerOperation( 'name', null, newRange, model.markers, false, 0 );
  1514. const transOp = transform( op, transformBy );
  1515. expect( transOp.length ).to.equal( 1 );
  1516. expectOperation( transOp[ 0 ], expected );
  1517. } );
  1518. } );
  1519. } );
  1520. describe( 'MoveOperation to graveyard', () => {
  1521. describe( 'by MoveOperation', () => {
  1522. it( 'should force removing content even if was less important', () => {
  1523. const op = new MoveOperation( new Position( root, [ 8 ] ), 2, new Position( doc.graveyard, [ 0 ] ), 0 );
  1524. const targetPosition = Position.createFromPosition( op.targetPosition );
  1525. const transformBy = new MoveOperation( new Position( root, [ 8 ] ), 2, new Position( root, [ 1 ] ), 0 );
  1526. const sourcePosition = Position.createFromPosition( transformBy.targetPosition );
  1527. const transOp = transform( op, transformBy );
  1528. expect( transOp.length ).to.equal( 1 );
  1529. expectOperation( transOp[ 0 ], {
  1530. type: MoveOperation,
  1531. howMany: 2,
  1532. sourcePosition,
  1533. targetPosition
  1534. } );
  1535. } );
  1536. } );
  1537. } );
  1538. describe( 'NoOperation', () => {
  1539. beforeEach( () => {
  1540. op = new NoOperation( 0 );
  1541. expected = {
  1542. type: NoOperation
  1543. };
  1544. } );
  1545. describe( 'by InsertOperation', () => {
  1546. it( 'no operation update', () => {
  1547. const transformBy = new InsertOperation(
  1548. new Position( root, [ 0 ] ),
  1549. 'a',
  1550. 0
  1551. );
  1552. const transOp = transform( op, transformBy );
  1553. expect( transOp.length ).to.equal( 1 );
  1554. expectOperation( transOp[ 0 ], expected );
  1555. } );
  1556. } );
  1557. describe( 'by AttributeOperation', () => {
  1558. it( 'no operation update', () => {
  1559. const transformBy = new AttributeOperation(
  1560. new Range(
  1561. new Position( root, [ 0 ] ),
  1562. new Position( root, [ 1 ] )
  1563. ),
  1564. 'foo',
  1565. 'bar',
  1566. 'xyz',
  1567. 0
  1568. );
  1569. const transOp = transform( op, transformBy );
  1570. expect( transOp.length ).to.equal( 1 );
  1571. expectOperation( transOp[ 0 ], expected );
  1572. } );
  1573. } );
  1574. describe( 'by RootAttributeOperation', () => {
  1575. it( 'no operation update', () => {
  1576. const transformBy = new RootAttributeOperation(
  1577. root,
  1578. 'foo',
  1579. null,
  1580. 'bar',
  1581. 0
  1582. );
  1583. const transOp = transform( op, transformBy );
  1584. expect( transOp.length ).to.equal( 1 );
  1585. expectOperation( transOp[ 0 ], expected );
  1586. } );
  1587. } );
  1588. describe( 'by MoveOperation', () => {
  1589. it( 'no operation update', () => {
  1590. const transformBy = new MoveOperation(
  1591. new Position( root, [ 0 ] ),
  1592. 2,
  1593. new Position( root, [ 1 ] ),
  1594. 0
  1595. );
  1596. const transOp = transform( op, transformBy );
  1597. expect( transOp.length ).to.equal( 1 );
  1598. expectOperation( transOp[ 0 ], expected );
  1599. } );
  1600. } );
  1601. describe( 'by NoOperation', () => {
  1602. it( 'no operation update', () => {
  1603. const transformBy = new NoOperation( 0 );
  1604. const transOp = transform( op, transformBy );
  1605. expect( transOp.length ).to.equal( 1 );
  1606. expectOperation( transOp[ 0 ], expected );
  1607. } );
  1608. } );
  1609. describe( 'by RenameOperation', () => {
  1610. it( 'no position update', () => {
  1611. const transformBy = new RenameOperation( new Position( root, [ 0, 2, 0 ] ), 'oldName', 'newName', 0 );
  1612. const transOp = transform( op, transformBy );
  1613. expect( transOp.length ).to.equal( 1 );
  1614. expectOperation( transOp[ 0 ], expected );
  1615. } );
  1616. } );
  1617. describe( 'by MarkerOperation', () => {
  1618. it( 'no position update', () => {
  1619. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 8 ] ) );
  1620. const transformBy = new MarkerOperation( 'name', null, newRange, model.markers, false, 0 );
  1621. const transOp = transform( op, transformBy );
  1622. expect( transOp.length ).to.equal( 1 );
  1623. expectOperation( transOp[ 0 ], expected );
  1624. } );
  1625. } );
  1626. } );
  1627. describe( 'RenameOperation', () => {
  1628. beforeEach( () => {
  1629. const position = new Position( root, [ 0, 2, 2 ] );
  1630. op = new RenameOperation( position, 'oldName', 'newName', 0 );
  1631. expected = {
  1632. position,
  1633. oldName: 'oldName',
  1634. newName: 'newName'
  1635. };
  1636. } );
  1637. describe( 'by InsertOperation', () => {
  1638. it( 'target before renamed element: offset update', () => {
  1639. const transformBy = new InsertOperation(
  1640. new Position( root, [ 0, 2, 1 ] ),
  1641. [ nodeA, nodeB ],
  1642. 0
  1643. );
  1644. const transOp = transform( op, transformBy );
  1645. expect( transOp.length ).to.equal( 1 );
  1646. expected.position.offset = 4;
  1647. expectOperation( transOp[ 0 ], expected );
  1648. } );
  1649. it( 'target after renamed element: no change', () => {
  1650. const transformBy = new InsertOperation(
  1651. new Position( root, [ 0, 2, 3 ] ),
  1652. [ nodeA, nodeB ],
  1653. 0
  1654. );
  1655. const transOp = transform( op, transformBy );
  1656. expect( transOp.length ).to.equal( 1 );
  1657. expectOperation( transOp[ 0 ], expected );
  1658. } );
  1659. it( 'target before a node on path to renamed element: path update', () => {
  1660. const transformBy = new InsertOperation(
  1661. new Position( root, [ 0, 1 ] ),
  1662. [ nodeA, nodeB ],
  1663. 0
  1664. );
  1665. const transOp = transform( op, transformBy );
  1666. expect( transOp.length ).to.equal( 1 );
  1667. expected.position.path = [ 0, 4, 2 ];
  1668. expectOperation( transOp[ 0 ], expected );
  1669. } );
  1670. it( 'target after a node on path to renamed element: no change', () => {
  1671. const transformBy = new InsertOperation(
  1672. new Position( root, [ 0, 3 ] ),
  1673. [ nodeA, nodeB ],
  1674. 0
  1675. );
  1676. const transOp = transform( op, transformBy );
  1677. expect( transOp.length ).to.equal( 1 );
  1678. expectOperation( transOp[ 0 ], expected );
  1679. } );
  1680. } );
  1681. describe( 'by AttributeOperation', () => {
  1682. it( 'no operation update', () => {
  1683. const transformBy = new AttributeOperation(
  1684. new Range(
  1685. new Position( root, [ 0, 2, 1 ] ),
  1686. new Position( root, [ 1, 3 ] )
  1687. ),
  1688. 'foo',
  1689. 'bar',
  1690. 'xyz',
  1691. 0
  1692. );
  1693. const transOp = transform( op, transformBy );
  1694. expect( transOp.length ).to.equal( 1 );
  1695. expectOperation( transOp[ 0 ], expected );
  1696. } );
  1697. } );
  1698. describe( 'by RootAttributeOperation', () => {
  1699. it( 'no operation update', () => {
  1700. const transformBy = new RootAttributeOperation(
  1701. root,
  1702. 'foo',
  1703. null,
  1704. 'bar',
  1705. 0
  1706. );
  1707. const transOp = transform( op, transformBy );
  1708. expect( transOp.length ).to.equal( 1 );
  1709. expectOperation( transOp[ 0 ], expected );
  1710. } );
  1711. } );
  1712. describe( 'by MarkerOperation', () => {
  1713. it( 'no operation update', () => {
  1714. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 8 ] ) );
  1715. const transformBy = new MarkerOperation( 'name', null, newRange, model.markers, false, 0 );
  1716. const transOp = transform( op, transformBy );
  1717. expect( transOp.length ).to.equal( 1 );
  1718. expectOperation( transOp[ 0 ], expected );
  1719. } );
  1720. } );
  1721. describe( 'by RenameOperation', () => {
  1722. it( 'different element: no change', () => {
  1723. const transformBy = new RenameOperation(
  1724. new Position( root, [ 0, 2, 1 ] ),
  1725. 'foo',
  1726. 'bar',
  1727. 0
  1728. );
  1729. const transOp = transform( op, transformBy );
  1730. expect( transOp.length ).to.equal( 1 );
  1731. expectOperation( transOp[ 0 ], expected );
  1732. } );
  1733. it( 'same element and is important: convert to NoOperation', () => {
  1734. const transformBy = new RenameOperation(
  1735. new Position( root, [ 0, 2, 2 ] ),
  1736. 'oldName',
  1737. 'otherName',
  1738. 0
  1739. );
  1740. const transOp = transform( op, transformBy );
  1741. expect( transOp.length ).to.equal( 1 );
  1742. expectOperation( transOp[ 0 ], {
  1743. type: NoOperation
  1744. } );
  1745. } );
  1746. it( 'same element and is not important: change old name to new name', () => {
  1747. const transformBy = new RenameOperation(
  1748. new Position( root, [ 0, 2, 2 ] ),
  1749. 'oldName',
  1750. 'otherName',
  1751. 0
  1752. );
  1753. const transOp = transform( op, transformBy, strongContext );
  1754. expect( transOp.length ).to.equal( 1 );
  1755. expected.oldName = 'otherName';
  1756. expectOperation( transOp[ 0 ], expected );
  1757. } );
  1758. } );
  1759. describe( 'by MoveOperation', () => {
  1760. it( 'moved range before renamed element: update offset', () => {
  1761. const transformBy = new MoveOperation(
  1762. new Position( root, [ 0, 2, 0 ] ),
  1763. 2,
  1764. new Position( root, [ 2, 5 ] ),
  1765. 0
  1766. );
  1767. const transOp = transform( op, transformBy );
  1768. expect( transOp.length ).to.equal( 1 );
  1769. expected.position.offset = 0;
  1770. expectOperation( transOp[ 0 ], expected );
  1771. } );
  1772. it( 'moved range before an element on path to renamed element: update path', () => {
  1773. const transformBy = new MoveOperation(
  1774. new Position( root, [ 0, 0 ] ),
  1775. 2,
  1776. new Position( root, [ 2, 5 ] ),
  1777. 0
  1778. );
  1779. const transOp = transform( op, transformBy );
  1780. expect( transOp.length ).to.equal( 1 );
  1781. expected.position.path = [ 0, 0, 2 ];
  1782. expectOperation( transOp[ 0 ], expected );
  1783. } );
  1784. it( 'moved range contains renamed element: update path', () => {
  1785. const transformBy = new MoveOperation(
  1786. new Position( root, [ 0, 2, 1 ] ),
  1787. 3,
  1788. new Position( root, [ 2, 5 ] ),
  1789. 0
  1790. );
  1791. const transOp = transform( op, transformBy );
  1792. expect( transOp.length ).to.equal( 1 );
  1793. expected.position.path = [ 2, 6 ];
  1794. expectOperation( transOp[ 0 ], expected );
  1795. } );
  1796. it( 'moved range contains renamed element parent: updated path', () => {
  1797. const transformBy = new MoveOperation(
  1798. new Position( root, [ 0, 1 ] ),
  1799. 3,
  1800. new Position( root, [ 2, 5 ] ),
  1801. 0
  1802. );
  1803. const transOp = transform( op, transformBy );
  1804. expect( transOp.length ).to.equal( 1 );
  1805. expected.position.path = [ 2, 6, 2 ];
  1806. expectOperation( transOp[ 0 ], expected );
  1807. } );
  1808. it( 'move target before renamed element: update offset', () => {
  1809. const transformBy = new MoveOperation(
  1810. new Position( root, [ 2, 5 ] ),
  1811. 2,
  1812. new Position( root, [ 0, 2, 1 ] ),
  1813. 0
  1814. );
  1815. const transOp = transform( op, transformBy );
  1816. expect( transOp.length ).to.equal( 1 );
  1817. expected.position.offset = 4;
  1818. expectOperation( transOp[ 0 ], expected );
  1819. } );
  1820. it( 'move target before an element on path to renamed element: update path', () => {
  1821. const transformBy = new MoveOperation(
  1822. new Position( root, [ 0, 2, 0 ] ),
  1823. 2,
  1824. new Position( root, [ 2, 5 ] ),
  1825. 0
  1826. );
  1827. const transOp = transform( op, transformBy );
  1828. expect( transOp.length ).to.equal( 1 );
  1829. expected.position.offset = 0;
  1830. expectOperation( transOp[ 0 ], expected );
  1831. } );
  1832. } );
  1833. } );
  1834. describe( 'MarkerOperation', () => {
  1835. let oldRange, newRange;
  1836. beforeEach( () => {
  1837. oldRange = Range.createFromParentsAndOffsets( root, 1, root, 4 );
  1838. newRange = Range.createFromParentsAndOffsets( root, 10, root, 12 );
  1839. op = new MarkerOperation( 'name', oldRange, newRange, model.markers, false, 0 );
  1840. expected = {
  1841. name: 'name',
  1842. oldRange,
  1843. newRange
  1844. };
  1845. } );
  1846. describe( 'by InsertOperation', () => {
  1847. it( 'insert position affecting oldRange: update oldRange', () => {
  1848. // Just CC things.
  1849. op.newRange = null;
  1850. const transformBy = new InsertOperation( Position.createAt( root, 0 ), [ nodeA, nodeB ], 0 );
  1851. const transOp = transform( op, transformBy );
  1852. expected.newRange = null;
  1853. expected.oldRange.start.offset = 3;
  1854. expected.oldRange.end.offset = 6;
  1855. expect( transOp.length ).to.equal( 1 );
  1856. expectOperation( transOp[ 0 ], expected );
  1857. } );
  1858. it( 'insert position affecting newRange: update newRange', () => {
  1859. // Just CC things.
  1860. op.oldRange = null;
  1861. const transformBy = new InsertOperation( Position.createAt( root, 8 ), [ nodeA, nodeB ], 0 );
  1862. const transOp = transform( op, transformBy );
  1863. expected.oldRange = null;
  1864. expected.newRange.start.offset = 12;
  1865. expected.newRange.end.offset = 14;
  1866. expect( transOp.length ).to.equal( 1 );
  1867. expectOperation( transOp[ 0 ], expected );
  1868. } );
  1869. } );
  1870. describe( 'by AttributeOperation', () => {
  1871. it( 'no operation update', () => {
  1872. const transformBy = new AttributeOperation(
  1873. new Range(
  1874. new Position( root, [ 2 ] ),
  1875. new Position( root, [ 11 ] )
  1876. ),
  1877. 'foo',
  1878. 'bar',
  1879. 'xyz',
  1880. 0
  1881. );
  1882. const transOp = transform( op, transformBy );
  1883. expect( transOp.length ).to.equal( 1 );
  1884. expectOperation( transOp[ 0 ], expected );
  1885. } );
  1886. } );
  1887. describe( 'by MoveOperation', () => {
  1888. it( 'moved range is before oldRange: update oldRange', () => {
  1889. // Just CC things.
  1890. op.newRange = null;
  1891. const transformBy = new MoveOperation( Position.createAt( root, 0 ), 1, Position.createAt( root, 20 ), 0 );
  1892. const transOp = transform( op, transformBy );
  1893. expected.newRange = null;
  1894. expected.oldRange.start.offset = 0;
  1895. expected.oldRange.end.offset = 3;
  1896. expect( transOp.length ).to.equal( 1 );
  1897. expectOperation( transOp[ 0 ], expected );
  1898. } );
  1899. it( 'moved range contains oldRange and is before newRange: update oldRange and newRange', () => {
  1900. const transformBy = new MoveOperation( Position.createAt( root, 2 ), 2, Position.createAt( root, 20 ), 0 );
  1901. const transOp = transform( op, transformBy );
  1902. expected.oldRange.start.offset = 1;
  1903. expected.oldRange.end.offset = 2;
  1904. expected.newRange.start.offset = 8;
  1905. expected.newRange.end.offset = 10;
  1906. expect( transOp.length ).to.equal( 1 );
  1907. expectOperation( transOp[ 0 ], expected );
  1908. } );
  1909. it( 'target position is inside newRange: update newRange', () => {
  1910. // Just CC things.
  1911. op.oldRange = null;
  1912. const transformBy = new MoveOperation( Position.createAt( root, 20 ), 2, Position.createAt( root, 11 ), 0 );
  1913. const transOp = transform( op, transformBy );
  1914. expected.oldRange = null;
  1915. expected.newRange.start.offset = 10;
  1916. expected.newRange.end.offset = 14;
  1917. expect( transOp.length ).to.equal( 1 );
  1918. expectOperation( transOp[ 0 ], expected );
  1919. } );
  1920. it( 'target position is inside oldRange and before newRange: update oldRange and newRange', () => {
  1921. const transformBy = new MoveOperation( Position.createAt( root, 20 ), 4, Position.createAt( root, 2 ), 0 );
  1922. const transOp = transform( op, transformBy );
  1923. expected.oldRange.start.offset = 1;
  1924. expected.oldRange.end.offset = 8;
  1925. expected.newRange.start.offset = 14;
  1926. expected.newRange.end.offset = 16;
  1927. expect( transOp.length ).to.equal( 1 );
  1928. expectOperation( transOp[ 0 ], expected );
  1929. } );
  1930. } );
  1931. describe( 'by RootAttributeOperation', () => {
  1932. it( 'no operation update', () => {
  1933. const transformBy = new RootAttributeOperation(
  1934. root,
  1935. 'foo',
  1936. null,
  1937. 'bar',
  1938. 0
  1939. );
  1940. const transOp = transform( op, transformBy );
  1941. expect( transOp.length ).to.equal( 1 );
  1942. expectOperation( transOp[ 0 ], expected );
  1943. } );
  1944. } );
  1945. describe( 'by RenameOperation', () => {
  1946. it( 'no operation update', () => {
  1947. const transformBy = new RenameOperation( new Position( root, [ 1 ] ), 'oldName', 'newName', 0 );
  1948. const transOp = transform( op, transformBy );
  1949. expect( transOp.length ).to.equal( 1 );
  1950. expectOperation( transOp[ 0 ], expected );
  1951. } );
  1952. } );
  1953. describe( 'by MarkerOperation', () => {
  1954. it( 'different marker name: no operation update', () => {
  1955. const transformBy = new MarkerOperation( 'otherName', oldRange, newRange, model.markers, false, 0 );
  1956. const transOp = transform( op, transformBy );
  1957. expect( transOp.length ).to.equal( 1 );
  1958. expectOperation( transOp[ 0 ], expected );
  1959. } );
  1960. it( 'same marker name and is important: convert to NoOperation', () => {
  1961. const anotherRange = Range.createFromParentsAndOffsets( root, 2, root, 2 );
  1962. const transformBy = new MarkerOperation( 'name', oldRange, anotherRange, model.markers, false, 0 );
  1963. const transOp = transform( op, transformBy );
  1964. expect( transOp.length ).to.equal( 1 );
  1965. expectOperation( transOp[ 0 ], {
  1966. type: NoOperation
  1967. } );
  1968. } );
  1969. it( 'same marker name and is less important: update oldRange parameter', () => {
  1970. const anotherRange = Range.createFromParentsAndOffsets( root, 2, root, 2 );
  1971. const transformBy = new MarkerOperation( 'name', oldRange, anotherRange, model.markers, false, 0 );
  1972. const transOp = transform( op, transformBy, strongContext );
  1973. expected.oldRange = anotherRange;
  1974. expect( transOp.length ).to.equal( 1 );
  1975. expectOperation( transOp[ 0 ], expected );
  1976. } );
  1977. } );
  1978. } );
  1979. } );