transform.js 101 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import transform from '../../../src/model/operation/transform';
  6. import Document from '../../../src/model/document';
  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 RemoveOperation from '../../../src/model/operation/removeoperation';
  17. import RenameOperation from '../../../src/model/operation/renameoperation';
  18. import NoOperation from '../../../src/model/operation/nooperation';
  19. describe( 'transform', () => {
  20. let doc, root, op, nodeA, nodeB, expected, baseVersion;
  21. beforeEach( () => {
  22. doc = new Document();
  23. root = doc.createRoot();
  24. nodeA = new Node();
  25. nodeB = new Node();
  26. baseVersion = 0;
  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. } else if ( params[ i ] instanceof Array ) {
  34. expect( op[ i ].length, i ).to.equal( params[ i ].length );
  35. for ( let j = 0; j < params[ i ].length; j++ ) {
  36. expect( op[ i ][ j ] ).to.equal( params[ i ][ j ] );
  37. }
  38. } else if ( params[ i ] instanceof Position || params[ i ] instanceof Range ) {
  39. expect( op[ i ].isEqual( params[ i ] ), i ).to.be.true;
  40. } else {
  41. expect( op[ i ], i ).to.equal( params[ i ] );
  42. }
  43. }
  44. }
  45. }
  46. function getPositionMovedInPath( position, index, howMany ) {
  47. const path = position.path.slice();
  48. path[ index ] += howMany;
  49. return new Position( position.root, path );
  50. }
  51. describe( 'InsertOperation', () => {
  52. let nodeC, nodeD, position;
  53. beforeEach( () => {
  54. nodeC = new Node();
  55. nodeD = new Node();
  56. position = new Position( root, [ 0, 2, 2 ] );
  57. op = new InsertOperation( position, [ nodeA, nodeB ], baseVersion );
  58. expected = {
  59. type: InsertOperation,
  60. position: Position.createFromPosition( position ),
  61. baseVersion: baseVersion + 1
  62. };
  63. } );
  64. describe( 'by InsertOperation', () => {
  65. it( 'target at different position: no position update', () => {
  66. const transformBy = new InsertOperation(
  67. new Position( root, [ 1, 3, 2 ] ),
  68. [ nodeC, nodeD ],
  69. baseVersion
  70. );
  71. const transOp = transform( op, transformBy );
  72. expect( transOp.length ).to.equal( 1 );
  73. expectOperation( transOp[ 0 ], expected );
  74. } );
  75. it( 'target at offset before: increment offset', () => {
  76. const transformBy = new InsertOperation(
  77. new Position( root, [ 0, 2, 0 ] ),
  78. [ nodeC, nodeD ],
  79. baseVersion
  80. );
  81. const transOp = transform( op, transformBy );
  82. expected.position = expected.position.getShiftedBy( 2 );
  83. expect( transOp.length ).to.equal( 1 );
  84. expectOperation( transOp[ 0 ], expected );
  85. } );
  86. it( 'target at same offset and is important: increment offset', () => {
  87. const transformBy = new InsertOperation(
  88. new Position( root, [ 0, 2, 2 ] ),
  89. [ nodeC, nodeD ],
  90. baseVersion
  91. );
  92. const transOp = transform( op, transformBy );
  93. expected.position = expected.position.getShiftedBy( 2 );
  94. expect( transOp.length ).to.equal( 1 );
  95. expectOperation( transOp[ 0 ], expected );
  96. } );
  97. it( 'target at same offset and is less important: no position update', () => {
  98. const transformBy = new InsertOperation(
  99. new Position( root, [ 0, 2, 2 ] ),
  100. [ nodeC, nodeD ],
  101. baseVersion
  102. );
  103. const transOp = transform( transformBy, { isStrong: true } );
  104. expect( transOp.length ).to.equal( 1 );
  105. expectOperation( transOp[ 0 ], expected );
  106. } );
  107. it( 'target at same offset and context.insertBefore = true: no position update', () => {
  108. const transformBy = new InsertOperation(
  109. new Position( root, [ 0, 2, 2 ] ),
  110. [ nodeC, nodeD ],
  111. baseVersion
  112. );
  113. const transOp = transform( transformBy, { isStrong: false, insertBefore: false } );
  114. expect( transOp.length ).to.equal( 1 );
  115. expectOperation( transOp[ 0 ], expected );
  116. } );
  117. it( 'target at same offset and context.insertBefore = false: increment offset', () => {
  118. const transformBy = new InsertOperation(
  119. new Position( root, [ 0, 2, 2 ] ),
  120. [ nodeC, nodeD ],
  121. baseVersion
  122. );
  123. const transOp = transform( op, transformBy );
  124. expected.position = expected.position.getShiftedBy( 2 );
  125. expect( transOp.length ).to.equal( 1 );
  126. expectOperation( transOp[ 0 ], expected );
  127. } );
  128. it( 'target at offset after: no position update', () => {
  129. const transformBy = new InsertOperation(
  130. new Position( root, [ 0, 2, 3 ] ),
  131. [ nodeC, nodeD ],
  132. baseVersion
  133. );
  134. const transOp = transform( op, transformBy );
  135. expect( transOp.length ).to.equal( 1 );
  136. expectOperation( transOp[ 0 ], expected );
  137. } );
  138. it( 'target before node from path: increment index on path', () => {
  139. const transformBy = new InsertOperation(
  140. new Position( root, [ 0, 1 ] ),
  141. [ nodeC, nodeD ],
  142. baseVersion
  143. );
  144. const transOp = transform( op, transformBy );
  145. expected.position = getPositionMovedInPath( expected.position, 1, 2 );
  146. expect( transOp.length ).to.equal( 1 );
  147. expectOperation( transOp[ 0 ], expected );
  148. } );
  149. it( 'target after node from path: no position update', () => {
  150. const transformBy = new InsertOperation(
  151. new Position( root, [ 0, 6 ] ),
  152. [ nodeC, nodeD ],
  153. baseVersion
  154. );
  155. const transOp = transform( op, transformBy );
  156. expect( transOp.length ).to.equal( 1 );
  157. expectOperation( transOp[ 0 ], expected );
  158. } );
  159. } );
  160. describe( 'by AttributeOperation', () => {
  161. it( 'no position update', () => {
  162. const transformBy = new AttributeOperation(
  163. Range.createFromPositionAndShift( position, 2 ),
  164. 'foo',
  165. null,
  166. 'bar',
  167. baseVersion
  168. );
  169. const transOp = transform( op, transformBy );
  170. expect( transOp.length ).to.equal( 1 );
  171. expectOperation( transOp[ 0 ], expected );
  172. } );
  173. } );
  174. describe( 'by RootAttributeOperation', () => {
  175. it( 'no position update', () => {
  176. const transformBy = new RootAttributeOperation(
  177. root,
  178. 'foo',
  179. null,
  180. 'bar',
  181. baseVersion
  182. );
  183. const transOp = transform( op, transformBy );
  184. expect( transOp.length ).to.equal( 1 );
  185. expectOperation( transOp[ 0 ], expected );
  186. } );
  187. } );
  188. describe( 'by MoveOperation', () => {
  189. it( 'range and target are different than insert position: no position update', () => {
  190. const transformBy = new MoveOperation(
  191. new Position( root, [ 1, 3, 2 ] ),
  192. 2,
  193. new Position( root, [ 2, 1 ] ),
  194. baseVersion
  195. );
  196. const transOp = transform( op, transformBy );
  197. expect( transOp.length ).to.equal( 1 );
  198. expectOperation( transOp[ 0 ], expected );
  199. } );
  200. it( 'range is before insert position offset: decrement offset', () => {
  201. const transformBy = new MoveOperation(
  202. new Position( root, [ 0, 2, 0 ] ),
  203. 1,
  204. new Position( root, [ 1, 1 ] ),
  205. baseVersion
  206. );
  207. const transOp = transform( op, transformBy );
  208. expected.position = expected.position.getShiftedBy( -1 );
  209. expect( transOp.length ).to.equal( 1 );
  210. expectOperation( transOp[ 0 ], expected );
  211. } );
  212. it( 'range offset is after insert position offset: no position update', () => {
  213. const transformBy = new MoveOperation(
  214. new Position( root, [ 0, 2, 5 ] ),
  215. 1,
  216. new Position( root, [ 1, 1 ] ),
  217. baseVersion
  218. );
  219. const transOp = transform( op, transformBy );
  220. expect( transOp.length ).to.equal( 1 );
  221. expectOperation( transOp[ 0 ], expected );
  222. } );
  223. it( 'target offset before insert position offset: increment offset', () => {
  224. const transformBy = new MoveOperation(
  225. new Position( root, [ 1, 1 ] ),
  226. 2,
  227. new Position( root, [ 0, 2, 0 ] ),
  228. baseVersion
  229. );
  230. const transOp = transform( op, transformBy );
  231. expected.position = expected.position.getShiftedBy( 2 );
  232. expect( transOp.length ).to.equal( 1 );
  233. expectOperation( transOp[ 0 ], expected );
  234. } );
  235. it( 'target offset after insert position offset: no position update', () => {
  236. const transformBy = new MoveOperation(
  237. new Position( root, [ 1, 1 ] ),
  238. 2,
  239. new Position( root, [ 0, 2, 5 ] ),
  240. baseVersion
  241. );
  242. const transOp = transform( op, transformBy );
  243. expect( transOp.length ).to.equal( 1 );
  244. expectOperation( transOp[ 0 ], expected );
  245. } );
  246. it( 'target offset same as insert position offset and is important: increment offset', () => {
  247. const transformBy = new MoveOperation(
  248. new Position( root, [ 1, 1 ] ),
  249. 2,
  250. new Position( root, [ 0, 2, 2 ] ),
  251. baseVersion
  252. );
  253. const transOp = transform( op, transformBy );
  254. expected.position = expected.position.getShiftedBy( 2 );
  255. expect( transOp.length ).to.equal( 1 );
  256. expectOperation( transOp[ 0 ], expected );
  257. } );
  258. it( 'target offset same as insert position offset and is less important: no position update', () => {
  259. const transformBy = new MoveOperation(
  260. new Position( root, [ 1, 1 ] ),
  261. 2,
  262. new Position( root, [ 0, 2, 2 ] ),
  263. baseVersion
  264. );
  265. const transOp = transform( op, transformBy, { isStrong: true } );
  266. expect( transOp.length ).to.equal( 1 );
  267. expectOperation( transOp[ 0 ], expected );
  268. } );
  269. it( 'target offset same as insert position offset and context.insertBefore = true: increment offset', () => {
  270. const transformBy = new MoveOperation(
  271. new Position( root, [ 1, 1 ] ),
  272. 2,
  273. new Position( root, [ 0, 2, 2 ] ),
  274. baseVersion
  275. );
  276. const transOp = transform( op, transformBy, { isStrong: true, insertBefore: true } );
  277. expected.position = expected.position.getShiftedBy( 2 );
  278. expect( transOp.length ).to.equal( 1 );
  279. expectOperation( transOp[ 0 ], expected );
  280. } );
  281. it( 'target offset same as insert position offset and context.insertBefore = false: no position update', () => {
  282. const transformBy = new MoveOperation(
  283. new Position( root, [ 1, 1 ] ),
  284. 2,
  285. new Position( root, [ 0, 2, 2 ] ),
  286. baseVersion
  287. );
  288. const transOp = transform( op, transformBy, { isStrong: false, insertBefore: false } );
  289. expect( transOp.length ).to.equal( 1 );
  290. expectOperation( transOp[ 0 ], expected );
  291. } );
  292. it( 'range is before node from insert position path: decrement index on path', () => {
  293. const transformBy = new MoveOperation(
  294. new Position( root, [ 0, 0 ] ),
  295. 1,
  296. new Position( root, [ 1, 0 ] ),
  297. baseVersion
  298. );
  299. const transOp = transform( op, transformBy );
  300. expected.position = getPositionMovedInPath( expected.position, 1, -1 );
  301. expect( transOp.length ).to.equal( 1 );
  302. expectOperation( transOp[ 0 ], expected );
  303. } );
  304. it( 'range is after node from insert position path: no position update', () => {
  305. const transformBy = new MoveOperation(
  306. new Position( root, [ 0, 4 ] ),
  307. 2,
  308. new Position( root, [ 1, 0 ] ),
  309. baseVersion
  310. );
  311. const transOp = transform( op, transformBy );
  312. expect( transOp.length ).to.equal( 1 );
  313. expectOperation( transOp[ 0 ], expected );
  314. } );
  315. it( 'target before node from insert position path: increment index on path', () => {
  316. const transformBy = new MoveOperation(
  317. new Position( root, [ 1, 0 ] ),
  318. 2,
  319. new Position( root, [ 0, 0 ] ),
  320. baseVersion
  321. );
  322. const transOp = transform( op, transformBy );
  323. expected.position = getPositionMovedInPath( expected.position, 1, 2 );
  324. expect( transOp.length ).to.equal( 1 );
  325. expectOperation( transOp[ 0 ], expected );
  326. } );
  327. it( 'target after node from insert position path: no position update', () => {
  328. const transformBy = new MoveOperation(
  329. new Position( root, [ 1, 0 ] ),
  330. 2,
  331. new Position( root, [ 0, 4 ] ),
  332. baseVersion
  333. );
  334. const transOp = transform( op, transformBy );
  335. expect( transOp.length ).to.equal( 1 );
  336. expectOperation( transOp[ 0 ], expected );
  337. } );
  338. it( 'range has node that contains insert position: update position', () => {
  339. const transformBy = new MoveOperation(
  340. new Position( root, [ 0, 1 ] ),
  341. 2,
  342. new Position( root, [ 1, 1 ] ),
  343. baseVersion
  344. );
  345. const transOp = transform( op, transformBy );
  346. expected.position = new Position( expected.position.root, [ 1, 2, 2 ] );
  347. expect( transOp.length ).to.equal( 1 );
  348. expectOperation( transOp[ 0 ], expected );
  349. } );
  350. it( 'range contains insert position (on same level): update position', () => {
  351. const transformBy = new MoveOperation(
  352. new Position( root, [ 0, 2, 0 ] ),
  353. 4,
  354. new Position( root, [ 1, 0 ] ),
  355. baseVersion
  356. );
  357. const transOp = transform( op, transformBy );
  358. expected.position = new Position( expected.position.root, [ 1, 2 ] );
  359. expect( transOp.length ).to.equal( 1 );
  360. expectOperation( transOp[ 0 ], expected );
  361. } );
  362. } );
  363. describe( 'by NoOperation', () => {
  364. it( 'no operation update', () => {
  365. const transformBy = new NoOperation( baseVersion );
  366. const transOp = transform( op, transformBy );
  367. expect( transOp.length ).to.equal( 1 );
  368. expectOperation( transOp[ 0 ], expected );
  369. } );
  370. } );
  371. describe( 'by RenameOperation', () => {
  372. it( 'no position update', () => {
  373. const transformBy = new RenameOperation( new Position( root, [ 0, 2, 0 ] ), 'oldName', 'newName', baseVersion );
  374. const transOp = transform( op, transformBy );
  375. expect( transOp.length ).to.equal( 1 );
  376. expectOperation( transOp[ 0 ], expected );
  377. } );
  378. } );
  379. describe( 'by MarkerOperation', () => {
  380. it( 'no position update', () => {
  381. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 4 ] ) );
  382. const transformBy = new MarkerOperation( 'name', null, newRange, doc.markers, baseVersion );
  383. const transOp = transform( op, transformBy );
  384. expect( transOp.length ).to.equal( 1 );
  385. expectOperation( transOp[ 0 ], expected );
  386. } );
  387. } );
  388. } );
  389. describe( 'AttributeOperation', () => {
  390. let start, end, range;
  391. beforeEach( () => {
  392. expected = {
  393. type: AttributeOperation,
  394. key: 'foo',
  395. oldValue: 'abc',
  396. newValue: 'bar',
  397. baseVersion: baseVersion + 1
  398. };
  399. } );
  400. describe( 'with multi-level range', () => {
  401. beforeEach( () => {
  402. start = new Position( root, [ 1, 2 ] );
  403. end = new Position( root, [ 2, 2, 4 ] );
  404. range = new Range( start, end );
  405. op = new AttributeOperation( range, 'foo', 'abc', 'bar', baseVersion );
  406. expected.range = new Range( start, end );
  407. } );
  408. describe( 'by InsertOperation', () => {
  409. it( 'target at different position: no operation update', () => {
  410. const transformBy = new InsertOperation(
  411. new Position( root, [ 3, 3, 2 ] ),
  412. [ nodeA, nodeB ],
  413. baseVersion
  414. );
  415. const transOp = transform( op, transformBy );
  416. expect( transOp.length ).to.equal( 1 );
  417. expectOperation( transOp[ 0 ], expected );
  418. } );
  419. it( 'target at offset before: increment offset', () => {
  420. const transformBy = new InsertOperation(
  421. new Position( root, [ 1, 1 ] ),
  422. [ nodeA, nodeB ],
  423. baseVersion
  424. );
  425. const transOp = transform( op, transformBy );
  426. expected.range = new Range(
  427. expected.range.start.getShiftedBy( 2 ),
  428. expected.range.end
  429. );
  430. expect( transOp.length ).to.equal( 1 );
  431. expectOperation( transOp[ 0 ], expected );
  432. } );
  433. it( 'target at same offset: increment offset', () => {
  434. const transformBy = new InsertOperation(
  435. new Position( root, [ 1, 2 ] ),
  436. [ nodeA, nodeB ],
  437. baseVersion
  438. );
  439. const transOp = transform( op, transformBy );
  440. expected.range = new Range(
  441. expected.range.start.getShiftedBy( 2 ),
  442. expected.range.end
  443. );
  444. expect( transOp.length ).to.equal( 1 );
  445. expectOperation( transOp[ 0 ], expected );
  446. } );
  447. it( 'target at offset after: no operation update', () => {
  448. const transformBy = new InsertOperation(
  449. new Position( root, [ 3, 2 ] ),
  450. [ nodeA, nodeB ],
  451. baseVersion
  452. );
  453. const transOp = transform( op, transformBy );
  454. expect( transOp.length ).to.equal( 1 );
  455. expectOperation( transOp[ 0 ], expected );
  456. } );
  457. it( 'target before node from path: increment index on path', () => {
  458. const transformBy = new InsertOperation(
  459. new Position( root, [ 0 ] ),
  460. [ nodeA, nodeB ],
  461. baseVersion
  462. );
  463. const transOp = transform( op, transformBy );
  464. expected.range = new Range(
  465. getPositionMovedInPath( expected.range.start, 0, 2 ),
  466. getPositionMovedInPath( expected.range.end, 0, 2 )
  467. );
  468. expect( transOp.length ).to.equal( 1 );
  469. expectOperation( transOp[ 0 ], expected );
  470. } );
  471. it( 'target after node from path: no position change', () => {
  472. const transformBy = new InsertOperation(
  473. new Position( root, [ 2, 6 ] ),
  474. [ nodeA, nodeB ],
  475. baseVersion
  476. );
  477. const transOp = transform( op, transformBy );
  478. expect( transOp.length ).to.equal( 1 );
  479. expectOperation( transOp[ 0 ], expected );
  480. } );
  481. it( 'target inside change range: split into two operations', () => {
  482. const transformBy = new InsertOperation(
  483. new Position( root, [ 1, 3, 1 ] ),
  484. [ nodeA, nodeB ],
  485. baseVersion
  486. );
  487. const transOp = transform( op, transformBy );
  488. expect( transOp.length ).to.equal( 2 );
  489. expected.range = new Range(
  490. new Position( expected.range.start.root, [ 1, 3, 3 ] ),
  491. expected.range.end
  492. );
  493. expectOperation( transOp[ 0 ], expected );
  494. expected.range = new Range(
  495. op.range.start,
  496. new Position( expected.range.end.root, [ 1, 3, 1 ] )
  497. );
  498. expected.baseVersion++;
  499. expectOperation( transOp[ 1 ], expected );
  500. } );
  501. } );
  502. describe( 'by AttributeOperation', () => {
  503. it( 'attributes have different key: no operation update', () => {
  504. const transformBy = new AttributeOperation(
  505. Range.createFromRange( range ),
  506. 'differentKey',
  507. true,
  508. false,
  509. baseVersion
  510. );
  511. const transOp = transform( op, transformBy );
  512. expect( transOp.length ).to.equal( 1 );
  513. expectOperation( transOp[ 0 ], expected );
  514. } );
  515. it( 'same key and value: convert to NoOperation', () => {
  516. const transformBy = new AttributeOperation(
  517. Range.createFromRange( range ),
  518. 'foo',
  519. 'abc',
  520. 'bar',
  521. baseVersion
  522. );
  523. const transOp = transform( op, transformBy );
  524. expect( transOp.length ).to.equal( 1 );
  525. expectOperation( transOp[ 0 ], {
  526. type: NoOperation,
  527. baseVersion: baseVersion + 1
  528. } );
  529. } );
  530. describe( 'that is less important and', () => {
  531. it( 'both operations removes same attribute: change old value to null', () => {
  532. op.newValue = null;
  533. expected.newValue = null;
  534. const transformBy = new AttributeOperation(
  535. new Range( new Position( root, [ 1, 1, 4 ] ), new Position( root, [ 3 ] ) ),
  536. 'foo',
  537. 'another',
  538. null,
  539. baseVersion
  540. );
  541. const transOp = transform( op, transformBy, { isStrong: true } );
  542. expected.oldValue = null;
  543. expect( transOp.length ).to.equal( 1 );
  544. } );
  545. it( 'range does not intersect original range: no operation update', () => {
  546. const transformBy = new AttributeOperation(
  547. new Range( new Position( root, [ 3, 0 ] ), new Position( root, [ 4 ] ) ),
  548. 'foo',
  549. 'abc',
  550. null,
  551. baseVersion
  552. );
  553. const transOp = transform( op, transformBy, { isStrong: true } );
  554. expect( transOp.length ).to.equal( 1 );
  555. expectOperation( transOp[ 0 ], expected );
  556. } );
  557. it( 'range contains original range: update oldValue', () => {
  558. const transformBy = new AttributeOperation(
  559. new Range( new Position( root, [ 1, 1, 4 ] ), new Position( root, [ 3 ] ) ),
  560. 'foo',
  561. 'abc',
  562. null,
  563. baseVersion
  564. );
  565. const transOp = transform( op, transformBy, { isStrong: true } );
  566. expected.oldValue = null;
  567. expect( transOp.length ).to.equal( 1 );
  568. expectOperation( transOp[ 0 ], expected );
  569. } );
  570. // [ original range < ] range transformed by >
  571. it( 'range intersects on left: split into two operations, update oldValue', () => {
  572. // Get more test cases and better code coverage
  573. op.newValue = null;
  574. expected.newValue = null;
  575. const transformBy = new AttributeOperation(
  576. new Range( new Position( root, [ 1, 4, 2 ] ), new Position( root, [ 3 ] ) ),
  577. 'foo',
  578. null,
  579. // Get more test cases and better code coverage
  580. 'another',
  581. baseVersion
  582. );
  583. const transOp = transform( op, transformBy, { isStrong: true } );
  584. expect( transOp.length ).to.equal( 2 );
  585. expected.range = new Range(
  586. expected.range.start,
  587. new Position( expected.range.end.root, [ 1, 4, 2 ] )
  588. );
  589. expectOperation( transOp[ 0 ], expected );
  590. expected.range = new Range(
  591. new Position( expected.range.start.root, [ 1, 4, 2 ] ),
  592. op.range.end
  593. );
  594. expected.oldValue = 'another';
  595. expected.baseVersion++;
  596. expectOperation( transOp[ 1 ], expected );
  597. } );
  598. // [ range transformed by < ] original range >
  599. it( 'range intersects on right: split into two operations, update oldValue', () => {
  600. const transformBy = new AttributeOperation(
  601. new Range( new Position( root, [ 1 ] ), new Position( root, [ 2, 1 ] ) ),
  602. 'foo',
  603. 'abc',
  604. null,
  605. baseVersion
  606. );
  607. const transOp = transform( op, transformBy, { isStrong: true } );
  608. expect( transOp.length ).to.equal( 2 );
  609. expected.range = new Range(
  610. new Position( expected.range.start.root, [ 2, 1 ] ),
  611. expected.range.end
  612. );
  613. expectOperation( transOp[ 0 ], expected );
  614. expected.range = new Range(
  615. op.range.start,
  616. new Position( expected.range.end.root, [ 2, 1 ] )
  617. );
  618. expected.oldValue = null;
  619. expected.baseVersion++;
  620. expectOperation( transOp[ 1 ], expected );
  621. } );
  622. it( 'range is inside original range: split into three operations, update oldValue', () => {
  623. const transformBy = new AttributeOperation(
  624. new Range( new Position( root, [ 1, 4, 1 ] ), new Position( root, [ 2, 1 ] ) ),
  625. 'foo',
  626. 'abc',
  627. null,
  628. baseVersion
  629. );
  630. const transOp = transform( op, transformBy, { isStrong: true } );
  631. expect( transOp.length ).to.equal( 3 );
  632. expected.range = new Range(
  633. expected.range.start,
  634. new Position( expected.range.end.root, [ 1, 4, 1 ] )
  635. );
  636. expectOperation( transOp[ 0 ], expected );
  637. expected.range = new Range(
  638. new Position( expected.range.start.root, [ 2, 1 ] ),
  639. op.range.end
  640. );
  641. expected.baseVersion++;
  642. expectOperation( transOp[ 1 ], expected );
  643. expected.range = new Range(
  644. new Position( expected.range.start.root, [ 1, 4, 1 ] ),
  645. new Position( expected.range.end.root, [ 2, 1 ] )
  646. );
  647. expected.oldValue = null;
  648. expected.baseVersion++;
  649. expectOperation( transOp[ 2 ], expected );
  650. } );
  651. } );
  652. describe( 'that is more important and', () => {
  653. it( 'both operations removes same attribute: convert to NoOperation', () => {
  654. op.newValue = null;
  655. expected.newValue = null;
  656. const transformBy = new AttributeOperation(
  657. new Range( new Position( root, [ 1, 1, 4 ] ), new Position( root, [ 3 ] ) ),
  658. 'foo',
  659. 'another',
  660. null,
  661. baseVersion
  662. );
  663. const transOp = transform( op, transformBy );
  664. expect( transOp.length ).to.equal( 1 );
  665. expectOperation( transOp[ 0 ], {
  666. type: NoOperation,
  667. baseVersion: baseVersion + 1
  668. } );
  669. } );
  670. it( 'range does not intersect original range: no operation update', () => {
  671. const transformBy = new AttributeOperation(
  672. new Range( new Position( root, [ 3, 0 ] ), new Position( root, [ 4 ] ) ),
  673. 'foo',
  674. 'abc',
  675. null,
  676. baseVersion
  677. );
  678. const transOp = transform( op, transformBy );
  679. expect( transOp.length ).to.equal( 1 );
  680. expectOperation( transOp[ 0 ], expected );
  681. } );
  682. it( 'range contains original range: convert to NoOperation', () => {
  683. const transformBy = new AttributeOperation(
  684. new Range( new Position( root, [ 1, 1, 4 ] ), new Position( root, [ 3 ] ) ),
  685. 'foo',
  686. 'abc',
  687. null,
  688. baseVersion
  689. );
  690. const transOp = transform( op, transformBy );
  691. expect( transOp.length ).to.equal( 1 );
  692. expectOperation( transOp[ 0 ], {
  693. type: NoOperation,
  694. baseVersion: baseVersion + 1
  695. } );
  696. } );
  697. // [ original range < ] range transformed by >
  698. it( 'range intersects on left: shrink original range', () => {
  699. const transformBy = new AttributeOperation(
  700. new Range( new Position( root, [ 1, 4, 2 ] ), new Position( root, [ 3 ] ) ),
  701. 'foo',
  702. 'abc',
  703. null,
  704. baseVersion
  705. );
  706. const transOp = transform( op, transformBy );
  707. expected.range = new Range(
  708. expected.range.start,
  709. new Position( expected.range.end.root, [ 1, 4, 2 ] )
  710. );
  711. expect( transOp.length ).to.equal( 1 );
  712. expectOperation( transOp[ 0 ], expected );
  713. } );
  714. // [ range transformed by < ] original range >
  715. it( 'range intersects on right: shrink original range', () => {
  716. const transformBy = new AttributeOperation(
  717. new Range( new Position( root, [ 1 ] ), new Position( root, [ 2, 1 ] ) ),
  718. 'foo',
  719. 'abc',
  720. null,
  721. baseVersion
  722. );
  723. const transOp = transform( op, transformBy );
  724. expected.range = new Range(
  725. new Position( expected.range.start.root, [ 2, 1 ] ),
  726. expected.range.end
  727. );
  728. expect( transOp.length ).to.equal( 1 );
  729. expectOperation( transOp[ 0 ], expected );
  730. } );
  731. it( 'range is inside original range: split into two operations', () => {
  732. const transformBy = new AttributeOperation(
  733. new Range( new Position( root, [ 1, 4, 1 ] ), new Position( root, [ 2, 1 ] ) ),
  734. 'foo',
  735. 'abc',
  736. null,
  737. baseVersion
  738. );
  739. const transOp = transform( op, transformBy );
  740. expect( transOp.length ).to.equal( 2 );
  741. expected.range = new Range(
  742. expected.range.start,
  743. new Position( expected.range.end.root, [ 1, 4, 1 ] )
  744. );
  745. expectOperation( transOp[ 0 ], expected );
  746. expected.range = new Range(
  747. new Position( expected.range.start.root, [ 2, 1 ] ),
  748. op.range.end
  749. );
  750. expected.baseVersion++;
  751. expectOperation( transOp[ 1 ], expected );
  752. } );
  753. } );
  754. } );
  755. describe( 'by RootAttributeOperation', () => {
  756. it( 'no operation update', () => {
  757. const transformBy = new RootAttributeOperation(
  758. root,
  759. 'foo',
  760. null,
  761. 'bar',
  762. baseVersion
  763. );
  764. const transOp = transform( op, transformBy );
  765. expect( transOp.length ).to.equal( 1 );
  766. expectOperation( transOp[ 0 ], expected );
  767. } );
  768. } );
  769. describe( 'by MoveOperation', () => {
  770. it( 'range and target are different than change range: no operation update', () => {
  771. const transformBy = new MoveOperation(
  772. new Position( root, [ 1, 1, 2 ] ),
  773. 2,
  774. new Position( root, [ 3, 4 ] ),
  775. baseVersion
  776. );
  777. const transOp = transform( op, transformBy );
  778. expect( transOp.length ).to.equal( 1 );
  779. expectOperation( transOp[ 0 ], expected );
  780. } );
  781. it( 'range offset is before change range start offset: decrement offset', () => {
  782. const transformBy = new MoveOperation(
  783. new Position( root, [ 1, 0 ] ),
  784. 2,
  785. new Position( root, [ 3, 4, 1 ] ),
  786. baseVersion
  787. );
  788. const transOp = transform( op, transformBy );
  789. expected.range = new Range(
  790. expected.range.start.getShiftedBy( -2 ),
  791. expected.range.end
  792. );
  793. expect( transOp.length ).to.equal( 1 );
  794. expectOperation( transOp[ 0 ], expected );
  795. } );
  796. it( 'target offset is before change range start offset: increment offset', () => {
  797. const transformBy = new MoveOperation(
  798. new Position( root, [ 3, 4, 1 ] ),
  799. 2,
  800. new Position( root, [ 1, 0 ] ),
  801. baseVersion
  802. );
  803. const transOp = transform( op, transformBy );
  804. expected.range = new Range(
  805. expected.range.start.getShiftedBy( 2 ),
  806. expected.range.end
  807. );
  808. expect( transOp.length ).to.equal( 1 );
  809. expectOperation( transOp[ 0 ], expected );
  810. } );
  811. it( 'range is before node from path to change range: decrement index on path', () => {
  812. const transformBy = new MoveOperation(
  813. new Position( root, [ 0 ] ),
  814. 1,
  815. new Position( root, [ 2, 4, 1 ] ),
  816. baseVersion
  817. );
  818. const transOp = transform( op, transformBy );
  819. expected.range = new Range(
  820. getPositionMovedInPath( expected.range.start, 0, -1 ),
  821. getPositionMovedInPath( expected.range.end, 0, -1 )
  822. );
  823. expect( transOp.length ).to.equal( 1 );
  824. expectOperation( transOp[ 0 ], expected );
  825. } );
  826. it( 'range is after node from path to change range: no position change', () => {
  827. const transformBy = new MoveOperation(
  828. new Position( root, [ 3 ] ),
  829. 2,
  830. new Position( root, [ 0, 1 ] ),
  831. baseVersion
  832. );
  833. const transOp = transform( op, transformBy );
  834. expect( transOp.length ).to.equal( 1 );
  835. expectOperation( transOp[ 0 ], expected );
  836. } );
  837. it( 'target before node from path to change range: increment index on path', () => {
  838. const transformBy = new MoveOperation(
  839. new Position( root, [ 3, 4, 1 ] ),
  840. 2,
  841. new Position( root, [ 1, 0 ] ),
  842. baseVersion
  843. );
  844. const transOp = transform( op, transformBy );
  845. expected.range = new Range(
  846. getPositionMovedInPath( expected.range.start, 1, 2 ),
  847. expected.range.end
  848. );
  849. expect( transOp.length ).to.equal( 1 );
  850. expectOperation( transOp[ 0 ], expected );
  851. } );
  852. it( 'target after node from path to change range: no position change', () => {
  853. const transformBy = new MoveOperation(
  854. new Position( root, [ 3, 4, 1 ] ),
  855. 2,
  856. new Position( root, [ 3 ] ),
  857. baseVersion
  858. );
  859. const transOp = transform( op, transformBy );
  860. expect( transOp.length ).to.equal( 1 );
  861. expectOperation( transOp[ 0 ], expected );
  862. } );
  863. it( 'range intersects on left with change range: split into two operations', () => {
  864. const transformBy = new MoveOperation(
  865. new Position( root, [ 2, 1 ] ),
  866. 3,
  867. new Position( root, [ 4 ] ),
  868. baseVersion
  869. );
  870. const transOp = transform( op, transformBy );
  871. expect( transOp.length ).to.equal( 2 );
  872. expected.range = new Range(
  873. expected.range.start,
  874. new Position( expected.range.end.root, [ 2, 1 ] )
  875. );
  876. expectOperation( transOp[ 0 ], expected );
  877. expected.range = new Range(
  878. new Position( expected.range.start.root, [ 4 ] ),
  879. new Position( expected.range.end.root, [ 5, 4 ] )
  880. );
  881. expected.baseVersion++;
  882. expectOperation( transOp[ 1 ], expected );
  883. } );
  884. it( 'range intersects on right with change range: split into two operation', () => {
  885. const transformBy = new MoveOperation(
  886. new Position( root, [ 1, 1 ] ),
  887. 3,
  888. new Position( root, [ 0, 0 ] ),
  889. baseVersion
  890. );
  891. const transOp = transform( op, transformBy );
  892. expect( transOp.length ).to.equal( 2 );
  893. expected.range = new Range(
  894. new Position( expected.range.start.root, [ 1, 1 ] ),
  895. expected.range.end
  896. );
  897. expectOperation( transOp[ 0 ], expected );
  898. expected.range = new Range(
  899. new Position( expected.range.start.root, [ 0, 1 ] ),
  900. new Position( expected.range.end.root, [ 0, 3 ] )
  901. );
  902. expected.baseVersion++;
  903. expectOperation( transOp[ 1 ], expected );
  904. } );
  905. it( 'range contains change range: update change range', () => {
  906. const transformBy = new MoveOperation(
  907. new Position( root, [ 1 ] ),
  908. 2,
  909. new Position( root, [ 3, 4, 1 ] ),
  910. baseVersion
  911. );
  912. const transOp = transform( op, transformBy );
  913. expected.range = new Range(
  914. new Position( expected.range.start.root, [ 1, 4, 1, 2 ] ),
  915. new Position( expected.range.end.root, [ 1, 4, 2, 2, 4 ] )
  916. );
  917. expect( transOp.length ).to.equal( 1 );
  918. expectOperation( transOp[ 0 ], expected );
  919. } );
  920. it( 'range is inside change range: split into two operations', () => {
  921. const transformBy = new MoveOperation(
  922. new Position( root, [ 1, 4 ] ),
  923. 2,
  924. new Position( root, [ 3, 2 ] ),
  925. baseVersion
  926. );
  927. const transOp = transform( op, transformBy );
  928. expect( transOp.length ).to.equal( 2 );
  929. expectOperation( transOp[ 0 ], expected );
  930. expected.range = new Range(
  931. new Position( expected.range.start.root, [ 3, 2 ] ),
  932. new Position( expected.range.end.root, [ 3, 4 ] )
  933. );
  934. expected.baseVersion++;
  935. expectOperation( transOp[ 1 ], expected );
  936. } );
  937. it( 'target inside change range: split into two operations', () => {
  938. const transformBy = new MoveOperation(
  939. new Position( root, [ 3, 4, 1 ] ),
  940. 2,
  941. new Position( root, [ 1, 4 ] ),
  942. baseVersion
  943. );
  944. const transOp = transform( op, transformBy );
  945. expect( transOp.length ).to.equal( 2 );
  946. expected.range = new Range(
  947. new Position( expected.range.start.root, [ 1, 6 ] ),
  948. expected.range.end
  949. );
  950. expectOperation( transOp[ 0 ], expected );
  951. expected.range = new Range(
  952. op.range.start,
  953. new Position( expected.range.end.root, [ 1, 4 ] )
  954. );
  955. expected.baseVersion++;
  956. expectOperation( transOp[ 1 ], expected );
  957. } );
  958. it( 'range intersects change range and target inside change range: split into three operations', () => {
  959. const transformBy = new MoveOperation(
  960. new Position( root, [ 1, 1 ] ),
  961. 3,
  962. new Position( root, [ 2 ] ),
  963. baseVersion
  964. );
  965. const transOp = transform( op, transformBy );
  966. expect( transOp.length ).to.equal( 3 );
  967. expected.range = new Range(
  968. new Position( expected.range.start.root, [ 5 ] ),
  969. new Position( expected.range.end.root, [ 5, 2, 4 ] )
  970. );
  971. expectOperation( transOp[ 0 ], expected );
  972. expected.range = new Range(
  973. new Position( expected.range.start.root, [ 1, 1 ] ),
  974. new Position( expected.range.end.root, [ 2 ] )
  975. );
  976. expected.baseVersion++;
  977. expectOperation( transOp[ 1 ], expected );
  978. expected.range = new Range(
  979. new Position( expected.range.start.root, [ 3 ] ),
  980. new Position( expected.range.end.root, [ 5 ] )
  981. );
  982. expected.baseVersion++;
  983. expectOperation( transOp[ 2 ], expected );
  984. } );
  985. } );
  986. describe( 'by NoOperation', () => {
  987. it( 'no operation update', () => {
  988. const transformBy = new NoOperation( baseVersion );
  989. const transOp = transform( op, transformBy );
  990. expect( transOp.length ).to.equal( 1 );
  991. expectOperation( transOp[ 0 ], expected );
  992. } );
  993. } );
  994. describe( 'by RenameOperation', () => {
  995. it( 'no position update', () => {
  996. const transformBy = new RenameOperation( new Position( root, [ 1, 0 ] ), 'oldName', 'newName', baseVersion );
  997. const transOp = transform( op, transformBy );
  998. expect( transOp.length ).to.equal( 1 );
  999. expectOperation( transOp[ 0 ], expected );
  1000. } );
  1001. } );
  1002. describe( 'by MarkerOperation', () => {
  1003. it( 'no operation update', () => {
  1004. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 8 ] ) );
  1005. const transformBy = new MarkerOperation( 'name', null, newRange, doc.markers, baseVersion );
  1006. const transOp = transform( op, transformBy );
  1007. expect( transOp.length ).to.equal( 1 );
  1008. expectOperation( transOp[ 0 ], expected );
  1009. } );
  1010. } );
  1011. } );
  1012. // Some extra cases for a AttributeOperation that operates on single tree level range.
  1013. // This means that the change range start and end differs only on offset value.
  1014. // This test suite also have some modifications to the original operation
  1015. // to get more test cases covered and better code coverage.
  1016. describe( 'with single-level range', () => {
  1017. beforeEach( () => {
  1018. start = new Position( root, [ 0, 2, 1 ] );
  1019. end = new Position( root, [ 0, 2, 4 ] );
  1020. range = new Range( start, end );
  1021. op = new AttributeOperation( range, 'foo', 'abc', 'bar', baseVersion );
  1022. expected.range = new Range( start, end );
  1023. } );
  1024. describe( 'by InsertOperation', () => {
  1025. it( 'target at offset before: increment offset', () => {
  1026. const transformBy = new InsertOperation(
  1027. new Position( root, [ 0, 2, 0 ] ),
  1028. [ nodeA, nodeB ],
  1029. baseVersion
  1030. );
  1031. const transOp = transform( op, transformBy );
  1032. expected.range = new Range(
  1033. expected.range.start.getShiftedBy( 2 ),
  1034. expected.range.end.getShiftedBy( 2 )
  1035. );
  1036. expect( transOp.length ).to.equal( 1 );
  1037. expectOperation( transOp[ 0 ], expected );
  1038. } );
  1039. it( 'target at same offset: increment offset', () => {
  1040. const transformBy = new InsertOperation(
  1041. new Position( root, [ 0, 2, 1 ] ),
  1042. [ nodeA, nodeB ],
  1043. baseVersion
  1044. );
  1045. const transOp = transform( op, transformBy );
  1046. expected.range = new Range(
  1047. expected.range.start.getShiftedBy( 2 ),
  1048. expected.range.end.getShiftedBy( 2 )
  1049. );
  1050. expect( transOp.length ).to.equal( 1 );
  1051. expectOperation( transOp[ 0 ], expected );
  1052. } );
  1053. } );
  1054. describe( 'by MoveOperation', () => {
  1055. it( 'range offset is before change range start offset: decrement offset', () => {
  1056. const transformBy = new MoveOperation(
  1057. new Position( root, [ 0, 2, 0 ] ),
  1058. 1,
  1059. new Position( root, [ 2, 4, 1 ] ),
  1060. baseVersion
  1061. );
  1062. const transOp = transform( op, transformBy );
  1063. expected.range = new Range(
  1064. expected.range.start.getShiftedBy( -1 ),
  1065. expected.range.end.getShiftedBy( -1 )
  1066. );
  1067. expect( transOp.length ).to.equal( 1 );
  1068. expectOperation( transOp[ 0 ], expected );
  1069. } );
  1070. it( 'target offset is before change range start offset: increment offset', () => {
  1071. const transformBy = new MoveOperation(
  1072. new Position( root, [ 2, 4, 1 ] ),
  1073. 2,
  1074. new Position( root, [ 0, 2, 0 ] ),
  1075. baseVersion
  1076. );
  1077. const transOp = transform( op, transformBy );
  1078. expected.range = new Range(
  1079. expected.range.start.getShiftedBy( 2 ),
  1080. expected.range.end.getShiftedBy( 2 )
  1081. );
  1082. expect( transOp.length ).to.equal( 1 );
  1083. expectOperation( transOp[ 0 ], expected );
  1084. } );
  1085. it( 'range intersects on left with change range: split into two operations', () => {
  1086. const transformBy = new MoveOperation(
  1087. new Position( root, [ 0, 2, 2 ] ),
  1088. 4,
  1089. new Position( root, [ 2, 4, 1 ] ),
  1090. baseVersion
  1091. );
  1092. const transOp = transform( op, transformBy );
  1093. expect( transOp.length ).to.equal( 2 );
  1094. expected.range = new Range(
  1095. expected.range.start,
  1096. expected.range.end.getShiftedBy( -2 )
  1097. );
  1098. expectOperation( transOp[ 0 ], expected );
  1099. expected.range = new Range(
  1100. new Position( expected.range.start.root, [ 2, 4, 1 ] ),
  1101. new Position( expected.range.end.root, [ 2, 4, 3 ] )
  1102. );
  1103. expected.baseVersion++;
  1104. expectOperation( transOp[ 1 ], expected );
  1105. } );
  1106. it( 'range intersects on right with change range: split into two operation', () => {
  1107. const transformBy = new MoveOperation(
  1108. new Position( root, [ 0, 2, 0 ] ),
  1109. 2,
  1110. new Position( root, [ 2, 4, 1 ] ),
  1111. baseVersion
  1112. );
  1113. const transOp = transform( op, transformBy );
  1114. expect( transOp.length ).to.equal( 2 );
  1115. expected.range = new Range(
  1116. expected.range.start.getShiftedBy( -1 ),
  1117. expected.range.end.getShiftedBy( -2 )
  1118. );
  1119. expectOperation( transOp[ 0 ], expected );
  1120. expected.range = new Range(
  1121. new Position( expected.range.start.root, [ 2, 4, 2 ] ),
  1122. new Position( expected.range.end.root, [ 2, 4, 3 ] )
  1123. );
  1124. expected.baseVersion++;
  1125. expectOperation( transOp[ 1 ], expected );
  1126. } );
  1127. it( 'range contains change range: update change range', () => {
  1128. const transformBy = new MoveOperation(
  1129. new Position( root, [ 0, 1 ] ),
  1130. 3,
  1131. new Position( root, [ 2, 4, 1 ] ),
  1132. baseVersion
  1133. );
  1134. const transOp = transform( op, transformBy );
  1135. expected.range = new Range(
  1136. new Position( expected.range.start.root, [ 2, 4, 2, 1 ] ),
  1137. new Position( expected.range.end.root, [ 2, 4, 2, 4 ] )
  1138. );
  1139. expect( transOp.length ).to.equal( 1 );
  1140. expectOperation( transOp[ 0 ], expected );
  1141. } );
  1142. it( 'range is inside change range: split into two operations', () => {
  1143. const transformBy = new MoveOperation(
  1144. new Position( root, [ 0, 2, 2 ] ),
  1145. 1,
  1146. new Position( root, [ 2, 4, 1 ] ),
  1147. baseVersion
  1148. );
  1149. const transOp = transform( op, transformBy );
  1150. expect( transOp.length ).to.equal( 2 );
  1151. expected.range = new Range(
  1152. expected.range.start,
  1153. expected.range.end.getShiftedBy( -1 )
  1154. );
  1155. expectOperation( transOp[ 0 ], expected );
  1156. expected.range = new Range(
  1157. new Position( expected.range.start.root, [ 2, 4, 1 ] ),
  1158. new Position( expected.range.end.root, [ 2, 4, 2 ] )
  1159. );
  1160. expected.baseVersion++;
  1161. expectOperation( transOp[ 1 ], expected );
  1162. } );
  1163. it( 'range is same as change range: update change range', () => {
  1164. const transformBy = new MoveOperation(
  1165. new Position( root, [ 0, 2, 1 ] ),
  1166. 3,
  1167. new Position( root, [ 2, 4, 1 ] ),
  1168. baseVersion
  1169. );
  1170. const transOp = transform( op, transformBy );
  1171. expected.range = new Range(
  1172. new Position( expected.range.start.root, [ 2, 4, 1 ] ),
  1173. new Position( expected.range.end.root, [ 2, 4, 4 ] )
  1174. );
  1175. expect( transOp.length ).to.equal( 1 );
  1176. expectOperation( transOp[ 0 ], expected );
  1177. } );
  1178. it( 'target inside change range: split into two operations', () => {
  1179. const transformBy = new MoveOperation(
  1180. new Position( root, [ 2, 4, 1 ] ),
  1181. 2,
  1182. new Position( root, [ 0, 2, 2 ] ),
  1183. baseVersion
  1184. );
  1185. const transOp = transform( op, transformBy );
  1186. expect( transOp.length ).to.equal( 2 );
  1187. expected.range = new Range(
  1188. expected.range.start.getShiftedTo( 4 ),
  1189. expected.range.end.getShiftedTo( 6 )
  1190. );
  1191. expectOperation( transOp[ 0 ], expected );
  1192. expected.range = new Range(
  1193. expected.range.start.getShiftedTo( op.range.start.offset ),
  1194. expected.range.end.getShiftedTo( 2 )
  1195. );
  1196. expected.baseVersion++;
  1197. expectOperation( transOp[ 1 ], expected );
  1198. } );
  1199. it( 'range intersects change range and target inside change range: split into three operations', () => {
  1200. const transformBy = new MoveOperation(
  1201. new Position( root, [ 0, 2, 0 ] ),
  1202. 2,
  1203. new Position( root, [ 0, 2, 3 ] ),
  1204. baseVersion
  1205. );
  1206. const transOp = transform( op, transformBy );
  1207. expect( transOp.length ).to.equal( 3 );
  1208. expected.range = new Range(
  1209. expected.range.start.getShiftedTo( 3 ),
  1210. expected.range.end.getShiftedTo( 4 )
  1211. );
  1212. expectOperation( transOp[ 0 ], expected );
  1213. expected.range = new Range(
  1214. expected.range.start.getShiftedTo( 0 ),
  1215. expected.range.end.getShiftedTo( 1 )
  1216. );
  1217. expected.baseVersion++;
  1218. expectOperation( transOp[ 1 ], expected );
  1219. expected.range = new Range(
  1220. expected.range.start.getShiftedTo( 2 ),
  1221. expected.range.end.getShiftedTo( 3 )
  1222. );
  1223. expected.baseVersion++;
  1224. expectOperation( transOp[ 2 ], expected );
  1225. } );
  1226. } );
  1227. } );
  1228. describe( 'by RemoveOperation', () => {
  1229. beforeEach( () => {
  1230. start = new Position( doc.graveyard, [ 2, 0 ] );
  1231. end = new Position( doc.graveyard, [ 2, 4 ] );
  1232. range = new Range( start, end );
  1233. op = new AttributeOperation( range, 'foo', 'abc', 'bar', baseVersion );
  1234. expected.range = new Range( start, end );
  1235. } );
  1236. it( 'remove operation inserted elements before attribute operation range: increment path', () => {
  1237. const transformBy = new RemoveOperation(
  1238. new Position( root, [ 0 ] ),
  1239. 2,
  1240. new Position( doc.graveyard, [ 0 ] ),
  1241. baseVersion
  1242. );
  1243. transformBy.targetPosition = new Position( transformBy.targetPosition.root, [ 0 ] );
  1244. const transOp = transform( op, transformBy );
  1245. expect( transOp.length ).to.equal( 1 );
  1246. expected.range = new Range(
  1247. new Position( expected.range.start.root, [ 4, 0 ] ),
  1248. new Position( expected.range.end.root, [ 4, 4 ] )
  1249. );
  1250. expectOperation( transOp[ 0 ], expected );
  1251. } );
  1252. it( 'remove operation inserted elements after attribute operation range: do nothing', () => {
  1253. const transformBy = new RemoveOperation(
  1254. new Position( root, [ 0 ] ),
  1255. 2,
  1256. new Position( doc.graveyard, [ 0 ] ),
  1257. baseVersion
  1258. );
  1259. transformBy.targetPosition = new Position( transformBy.targetPosition.root, [ 4 ] );
  1260. const transOp = transform( op, transformBy );
  1261. expect( transOp.length ).to.equal( 1 );
  1262. expectOperation( transOp[ 0 ], expected );
  1263. } );
  1264. } );
  1265. } );
  1266. describe( 'RootAttributeOperation', () => {
  1267. const diffRoot = new RootElement( null );
  1268. beforeEach( () => {
  1269. expected = {
  1270. type: RootAttributeOperation,
  1271. key: 'foo',
  1272. oldValue: 'abc',
  1273. newValue: 'bar',
  1274. baseVersion: baseVersion + 1
  1275. };
  1276. op = new RootAttributeOperation( root, 'foo', 'abc', 'bar', baseVersion );
  1277. } );
  1278. describe( 'by InsertOperation', () => {
  1279. it( 'no operation update', () => {
  1280. const transformBy = new InsertOperation(
  1281. new Position( root, [ 0 ] ),
  1282. 'a',
  1283. baseVersion
  1284. );
  1285. const transOp = transform( op, transformBy );
  1286. expect( transOp.length ).to.equal( 1 );
  1287. expectOperation( transOp[ 0 ], expected );
  1288. } );
  1289. } );
  1290. describe( 'by AttributeOperation', () => {
  1291. it( 'no operation update', () => {
  1292. const transformBy = new AttributeOperation(
  1293. new Range(
  1294. new Position( root, [ 0 ] ),
  1295. new Position( root, [ 1 ] )
  1296. ),
  1297. 'foo',
  1298. 'bar',
  1299. 'xyz',
  1300. baseVersion
  1301. );
  1302. const transOp = transform( op, transformBy );
  1303. expect( transOp.length ).to.equal( 1 );
  1304. expectOperation( transOp[ 0 ], expected );
  1305. } );
  1306. } );
  1307. describe( 'by RootAttributeOperation', () => {
  1308. it( 'changes different root: no operation update', () => {
  1309. const transformBy = new RootAttributeOperation(
  1310. diffRoot,
  1311. 'foo',
  1312. 'abc',
  1313. 'xyz',
  1314. baseVersion
  1315. );
  1316. const transOp = transform( op, transformBy );
  1317. expect( transOp.length ).to.equal( 1 );
  1318. expectOperation( transOp[ 0 ], expected );
  1319. } );
  1320. it( 'changes different key: no operation update', () => {
  1321. const transformBy = new RootAttributeOperation(
  1322. root,
  1323. 'abc',
  1324. 'abc',
  1325. 'xyz',
  1326. baseVersion
  1327. );
  1328. const transOp = transform( op, transformBy );
  1329. expect( transOp.length ).to.equal( 1 );
  1330. expectOperation( transOp[ 0 ], expected );
  1331. } );
  1332. it( 'sets same value for same key: convert to NoOperation', () => {
  1333. const transformBy = new RootAttributeOperation(
  1334. root,
  1335. 'foo',
  1336. 'abc',
  1337. 'bar',
  1338. baseVersion
  1339. );
  1340. const transOp = transform( op, transformBy );
  1341. expect( transOp.length ).to.equal( 1 );
  1342. expectOperation( transOp[ 0 ], {
  1343. type: NoOperation,
  1344. baseVersion: baseVersion + 1
  1345. } );
  1346. } );
  1347. it( 'sets different value for same key on same root and is important: no operation update', () => {
  1348. const transformBy = new RootAttributeOperation(
  1349. root,
  1350. 'foo',
  1351. 'abc',
  1352. 'xyz',
  1353. baseVersion
  1354. );
  1355. const transOp = transform( op, transformBy );
  1356. expect( transOp.length ).to.equal( 1 );
  1357. expectOperation( transOp[ 0 ], {
  1358. type: NoOperation,
  1359. baseVersion: baseVersion + 1
  1360. } );
  1361. } );
  1362. it( 'sets different value for same key on same root and is less important: convert to NoOperation', () => {
  1363. const transformBy = new RootAttributeOperation(
  1364. root,
  1365. 'foo',
  1366. 'abc',
  1367. 'xyz',
  1368. baseVersion
  1369. );
  1370. const transOp = transform( op, transformBy, { isStrong: true } );
  1371. expect( transOp.length ).to.equal( 1 );
  1372. expectOperation( transOp[ 0 ], expected );
  1373. } );
  1374. } );
  1375. describe( 'by MoveOperation', () => {
  1376. it( 'no operation update', () => {
  1377. const transformBy = new MoveOperation(
  1378. new Position( root, [ 0 ] ),
  1379. 2,
  1380. new Position( root, [ 1 ] ),
  1381. baseVersion
  1382. );
  1383. const transOp = transform( op, transformBy );
  1384. expect( transOp.length ).to.equal( 1 );
  1385. expectOperation( transOp[ 0 ], expected );
  1386. } );
  1387. } );
  1388. describe( 'by NoOperation', () => {
  1389. it( 'no operation update', () => {
  1390. const transformBy = new NoOperation( baseVersion );
  1391. const transOp = transform( op, transformBy );
  1392. expect( transOp.length ).to.equal( 1 );
  1393. expectOperation( transOp[ 0 ], expected );
  1394. } );
  1395. } );
  1396. describe( 'by RenameOperation', () => {
  1397. it( 'no position update', () => {
  1398. const transformBy = new RenameOperation( new Position( root, [ 0 ] ), 'oldName', 'newName', baseVersion );
  1399. const transOp = transform( op, transformBy );
  1400. expect( transOp.length ).to.equal( 1 );
  1401. expectOperation( transOp[ 0 ], expected );
  1402. } );
  1403. } );
  1404. describe( 'by MarkerOperation', () => {
  1405. it( 'no position update', () => {
  1406. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 8 ] ) );
  1407. const transformBy = new MarkerOperation( 'name', null, newRange, doc.markers, baseVersion );
  1408. const transOp = transform( op, transformBy );
  1409. expect( transOp.length ).to.equal( 1 );
  1410. expectOperation( transOp[ 0 ], expected );
  1411. } );
  1412. } );
  1413. } );
  1414. describe( 'MoveOperation', () => {
  1415. let sourcePosition, targetPosition, rangeEnd, howMany;
  1416. beforeEach( () => {
  1417. sourcePosition = new Position( root, [ 2, 2, 4 ] );
  1418. targetPosition = new Position( root, [ 3, 3, 3 ] );
  1419. howMany = 2;
  1420. rangeEnd = sourcePosition.getShiftedBy( howMany );
  1421. op = new MoveOperation( sourcePosition, howMany, targetPosition, baseVersion );
  1422. expected = {
  1423. type: MoveOperation,
  1424. sourcePosition: Position.createFromPosition( sourcePosition ),
  1425. targetPosition: Position.createFromPosition( targetPosition ),
  1426. howMany,
  1427. baseVersion: baseVersion + 1
  1428. };
  1429. } );
  1430. describe( 'by InsertOperation', () => {
  1431. it( 'target at different position than move range and target: no operation update', () => {
  1432. const transformBy = new InsertOperation(
  1433. new Position( root, [ 1, 3, 2 ] ),
  1434. [ nodeA, nodeB ],
  1435. baseVersion
  1436. );
  1437. const transOp = transform( op, transformBy );
  1438. expect( transOp.length ).to.equal( 1 );
  1439. expectOperation( transOp[ 0 ], expected );
  1440. } );
  1441. it( 'target inside node from move range: no operation update', () => {
  1442. const transformBy = new InsertOperation(
  1443. new Position( root, [ 2, 2, 4, 1 ] ),
  1444. [ nodeA, nodeB ],
  1445. baseVersion
  1446. );
  1447. const transOp = transform( op, transformBy );
  1448. expect( transOp.length ).to.equal( 1 );
  1449. expectOperation( transOp[ 0 ], expected );
  1450. } );
  1451. it( 'target at offset before range offset: increment range offset', () => {
  1452. const transformBy = new InsertOperation(
  1453. new Position( root, [ 2, 2, 0 ] ),
  1454. [ nodeA, nodeB ],
  1455. baseVersion
  1456. );
  1457. const transOp = transform( op, transformBy );
  1458. expected.sourcePosition = expected.sourcePosition.getShiftedBy( 2 );
  1459. expect( transOp.length ).to.equal( 1 );
  1460. expectOperation( transOp[ 0 ], expected );
  1461. } );
  1462. it( 'target at offset after range offset: no operation update', () => {
  1463. const transformBy = new InsertOperation(
  1464. new Position( root, [ 2, 2, 7 ] ),
  1465. [ nodeA, nodeB ],
  1466. baseVersion
  1467. );
  1468. const transOp = transform( op, transformBy );
  1469. expect( transOp.length ).to.equal( 1 );
  1470. expectOperation( transOp[ 0 ], expected );
  1471. } );
  1472. it( 'target before node from range start path: increment index on range start path', () => {
  1473. const transformBy = new InsertOperation(
  1474. new Position( root, [ 2, 0 ] ),
  1475. [ nodeA, nodeB ],
  1476. baseVersion
  1477. );
  1478. const transOp = transform( op, transformBy );
  1479. expected.sourcePosition = getPositionMovedInPath( expected.sourcePosition, 1, 2 );
  1480. expect( transOp.length ).to.equal( 1 );
  1481. expectOperation( transOp[ 0 ], expected );
  1482. } );
  1483. it( 'target after node from range start path: no operation update', () => {
  1484. const transformBy = new InsertOperation(
  1485. new Position( root, [ 2, 3 ] ),
  1486. [ nodeA, nodeB ],
  1487. baseVersion
  1488. );
  1489. const transOp = transform( op, transformBy );
  1490. expect( transOp.length ).to.equal( 1 );
  1491. expectOperation( transOp[ 0 ], expected );
  1492. } );
  1493. it( 'target offset before move target offset: increment target offset', () => {
  1494. const transformBy = new InsertOperation(
  1495. new Position( root, [ 3, 3, 2 ] ),
  1496. [ nodeA, nodeB ],
  1497. baseVersion
  1498. );
  1499. const transOp = transform( op, transformBy );
  1500. expected.targetPosition = expected.targetPosition.getShiftedBy( 2 );
  1501. expect( transOp.length ).to.equal( 1 );
  1502. expectOperation( transOp[ 0 ], expected );
  1503. } );
  1504. it( 'target offset after move target offset: no operation update', () => {
  1505. const transformBy = new InsertOperation(
  1506. new Position( root, [ 3, 3, 4 ] ),
  1507. [ nodeA, nodeB ],
  1508. baseVersion
  1509. );
  1510. const transOp = transform( op, transformBy );
  1511. expect( transOp.length ).to.equal( 1 );
  1512. expectOperation( transOp[ 0 ], expected );
  1513. } );
  1514. it( 'target before node from move target position path: increment index on move target position path', () => {
  1515. const transformBy = new InsertOperation(
  1516. new Position( root, [ 3, 3 ] ),
  1517. [ nodeA, nodeB ],
  1518. baseVersion
  1519. );
  1520. const transOp = transform( op, transformBy );
  1521. expected.targetPosition = getPositionMovedInPath( expected.targetPosition, 1, 2 );
  1522. expect( transOp.length ).to.equal( 1 );
  1523. expectOperation( transOp[ 0 ], expected );
  1524. } );
  1525. it( 'target after node from move target position path: no operation update', () => {
  1526. const transformBy = new InsertOperation(
  1527. new Position( root, [ 3, 6 ] ),
  1528. [ nodeA, nodeB ],
  1529. baseVersion
  1530. );
  1531. const transOp = transform( op, transformBy );
  1532. expect( transOp.length ).to.equal( 1 );
  1533. expectOperation( transOp[ 0 ], expected );
  1534. } );
  1535. it( 'target offset same as move target offset and is important: increment target offset', () => {
  1536. const transformBy = new InsertOperation(
  1537. new Position( root, [ 3, 3, 3 ] ),
  1538. [ nodeA, nodeB ],
  1539. baseVersion
  1540. );
  1541. const transOp = transform( op, transformBy );
  1542. expected.targetPosition = expected.targetPosition.getShiftedBy( 2 );
  1543. expect( transOp.length ).to.equal( 1 );
  1544. expectOperation( transOp[ 0 ], expected );
  1545. } );
  1546. it( 'target offset same as move target offset and is less important: no operation update', () => {
  1547. const transformBy = new InsertOperation(
  1548. new Position( root, [ 3, 3, 3 ] ),
  1549. [ nodeA, nodeB ],
  1550. baseVersion
  1551. );
  1552. const transOp = transform( op, transformBy, { isStrong: true } );
  1553. expect( transOp.length ).to.equal( 1 );
  1554. expectOperation( transOp[ 0 ], expected );
  1555. } );
  1556. it( 'target offset same as move target offset and context.insertBefore = true: increment target offset', () => {
  1557. const transformBy = new InsertOperation(
  1558. new Position( root, [ 3, 3, 3 ] ),
  1559. [ nodeA, nodeB ],
  1560. baseVersion
  1561. );
  1562. const transOp = transform( op, transformBy, { isStrong: true, insertBefore: true } );
  1563. expected.targetPosition = expected.targetPosition.getShiftedBy( 2 );
  1564. expect( transOp.length ).to.equal( 1 );
  1565. expectOperation( transOp[ 0 ], expected );
  1566. } );
  1567. it( 'target offset same as move target offset and context.insertBefore = false: no operation update', () => {
  1568. const transformBy = new InsertOperation(
  1569. new Position( root, [ 3, 3, 3 ] ),
  1570. [ nodeA, nodeB ],
  1571. baseVersion
  1572. );
  1573. const transOp = transform( op, transformBy, { isStrong: false, insertBefore: false } );
  1574. expect( transOp.length ).to.equal( 1 );
  1575. expectOperation( transOp[ 0 ], expected );
  1576. } );
  1577. it( 'target inside move range: expand range', () => {
  1578. const transformBy = new InsertOperation(
  1579. new Position( root, [ 2, 2, 5 ] ),
  1580. [ nodeA, nodeB ],
  1581. baseVersion
  1582. );
  1583. const transOp = transform( op, transformBy );
  1584. expect( transOp.length ).to.equal( 1 );
  1585. expected.howMany = 4;
  1586. expectOperation( transOp[ 0 ], expected );
  1587. } );
  1588. it( 'target at offset same as range end boundary: no operation update', () => {
  1589. const transformBy = new InsertOperation(
  1590. new Position( root, [ 2, 2, 6 ] ),
  1591. [ nodeA, nodeB ],
  1592. baseVersion
  1593. );
  1594. const transOp = transform( op, transformBy );
  1595. expect( transOp.length ).to.equal( 1 );
  1596. expectOperation( transOp[ 0 ], expected );
  1597. } );
  1598. it( 'target at offset same as range end boundary (sticky move): expand range', () => {
  1599. const transformBy = new InsertOperation(
  1600. new Position( root, [ 2, 2, 6 ] ),
  1601. [ nodeA, nodeB ],
  1602. baseVersion
  1603. );
  1604. op.isSticky = true;
  1605. const transOp = transform( op, transformBy );
  1606. expected.howMany = 4;
  1607. expect( transOp.length ).to.equal( 1 );
  1608. expectOperation( transOp[ 0 ], expected );
  1609. } );
  1610. } );
  1611. describe( 'by AttributeOperation', () => {
  1612. it( 'no operation update', () => {
  1613. const transformBy = new AttributeOperation(
  1614. new Range( sourcePosition, rangeEnd ),
  1615. 'abc',
  1616. true,
  1617. false,
  1618. baseVersion
  1619. );
  1620. const transOp = transform( op, transformBy );
  1621. expect( transOp.length ).to.equal( 1 );
  1622. expectOperation( transOp[ 0 ], expected );
  1623. } );
  1624. } );
  1625. describe( 'by RootAttributeOperation', () => {
  1626. it( 'no operation update', () => {
  1627. const transformBy = new RootAttributeOperation(
  1628. root,
  1629. 'foo',
  1630. null,
  1631. 'bar',
  1632. baseVersion
  1633. );
  1634. const transOp = transform( op, transformBy );
  1635. expect( transOp.length ).to.equal( 1 );
  1636. expectOperation( transOp[ 0 ], expected );
  1637. } );
  1638. } );
  1639. describe( 'by MoveOperation', () => {
  1640. it( 'range and target different than transforming range and target: no operation update', () => {
  1641. const transformBy = new MoveOperation(
  1642. new Position( root, [ 1, 2 ] ),
  1643. 3,
  1644. new Position( root, [ 4, 1, 0 ] ),
  1645. baseVersion
  1646. );
  1647. const transOp = transform( op, transformBy );
  1648. expect( transOp.length ).to.equal( 1 );
  1649. expectOperation( transOp[ 0 ], expected );
  1650. } );
  1651. it( 'target offset before transforming range start offset: increment range offset', () => {
  1652. const transformBy = new MoveOperation(
  1653. new Position( root, [ 4, 1, 0 ] ),
  1654. 2,
  1655. new Position( root, [ 2, 2, 0 ] ),
  1656. baseVersion
  1657. );
  1658. const transOp = transform( op, transformBy );
  1659. expected.sourcePosition = expected.sourcePosition.getShiftedBy( 2 );
  1660. expect( transOp.length ).to.equal( 1 );
  1661. expectOperation( transOp[ 0 ], expected );
  1662. } );
  1663. it( 'target offset after transforming range start offset: no operation update', () => {
  1664. const transformBy = new MoveOperation(
  1665. new Position( root, [ 4, 1, 0 ] ),
  1666. 2,
  1667. new Position( root, [ 2, 2, 7 ] ),
  1668. baseVersion
  1669. );
  1670. const transOp = transform( op, transformBy );
  1671. expect( transOp.length ).to.equal( 1 );
  1672. expectOperation( transOp[ 0 ], expected );
  1673. } );
  1674. it( 'range start offset before transforming range start offset: decrement range offset', () => {
  1675. const transformBy = new MoveOperation(
  1676. new Position( root, [ 2, 2, 0 ] ),
  1677. 2,
  1678. new Position( root, [ 4, 1, 0 ] ),
  1679. baseVersion
  1680. );
  1681. const transOp = transform( op, transformBy );
  1682. expected.sourcePosition = expected.sourcePosition.getShiftedBy( -2 );
  1683. expect( transOp.length ).to.equal( 1 );
  1684. expectOperation( transOp[ 0 ], expected );
  1685. } );
  1686. it( 'range start offset after transforming range start offset: no operation update', () => {
  1687. const transformBy = new MoveOperation(
  1688. new Position( root, [ 2, 2, 9 ] ),
  1689. 2,
  1690. new Position( root, [ 4, 1, 0 ] ),
  1691. baseVersion
  1692. );
  1693. const transOp = transform( op, transformBy, { isStrong: true } );
  1694. expect( transOp.length ).to.equal( 1 );
  1695. expectOperation( transOp[ 0 ], expected );
  1696. } );
  1697. it( 'target before node from transforming range start path: increment index on range start path', () => {
  1698. const transformBy = new MoveOperation(
  1699. new Position( root, [ 4, 1, 0 ] ),
  1700. 2,
  1701. new Position( root, [ 2, 1 ] ),
  1702. baseVersion
  1703. );
  1704. const transOp = transform( op, transformBy );
  1705. expected.sourcePosition = getPositionMovedInPath( expected.sourcePosition, 1, 2 );
  1706. expect( transOp.length ).to.equal( 1 );
  1707. expectOperation( transOp[ 0 ], expected );
  1708. } );
  1709. it( 'target after node from transforming range start path: no operation update', () => {
  1710. const transformBy = new MoveOperation(
  1711. new Position( root, [ 4, 1, 0 ] ),
  1712. 2,
  1713. new Position( root, [ 2, 5 ] ),
  1714. baseVersion
  1715. );
  1716. const transOp = transform( op, transformBy );
  1717. expect( transOp.length ).to.equal( 1 );
  1718. expectOperation( transOp[ 0 ], expected );
  1719. } );
  1720. it( 'range before node from transforming range start path: decrement index on range start path', () => {
  1721. const transformBy = new MoveOperation(
  1722. new Position( root, [ 2, 0 ] ),
  1723. 1,
  1724. new Position( root, [ 4, 1, 0 ] ),
  1725. baseVersion
  1726. );
  1727. const transOp = transform( op, transformBy );
  1728. expected.sourcePosition = getPositionMovedInPath( expected.sourcePosition, 1, -1 );
  1729. expect( transOp.length ).to.equal( 1 );
  1730. expectOperation( transOp[ 0 ], expected );
  1731. } );
  1732. it( 'range after node from transforming range start path: no operation update', () => {
  1733. const transformBy = new MoveOperation(
  1734. new Position( root, [ 2, 3 ] ),
  1735. 2,
  1736. new Position( root, [ 4, 1, 0 ] ),
  1737. baseVersion
  1738. );
  1739. const transOp = transform( op, transformBy );
  1740. expect( transOp.length ).to.equal( 1 );
  1741. expectOperation( transOp[ 0 ], expected );
  1742. } );
  1743. it( 'target offset before transforming target offset: increment target offset', () => {
  1744. const transformBy = new MoveOperation(
  1745. new Position( root, [ 4, 1, 0 ] ),
  1746. 2,
  1747. new Position( root, [ 3, 3, 0 ] ),
  1748. baseVersion
  1749. );
  1750. const transOp = transform( op, transformBy );
  1751. expected.targetPosition = expected.targetPosition.getShiftedBy( 2 );
  1752. expect( transOp.length ).to.equal( 1 );
  1753. expectOperation( transOp[ 0 ], expected );
  1754. } );
  1755. it( 'target offset after transforming target offset: no operation update', () => {
  1756. const transformBy = new MoveOperation(
  1757. new Position( root, [ 4, 1, 0 ] ),
  1758. 2,
  1759. new Position( root, [ 3, 3, 5 ] ),
  1760. baseVersion
  1761. );
  1762. const transOp = transform( op, transformBy );
  1763. expect( transOp.length ).to.equal( 1 );
  1764. expectOperation( transOp[ 0 ], expected );
  1765. } );
  1766. it( 'range offset before transforming target offset: decrement target offset', () => {
  1767. const transformBy = new MoveOperation(
  1768. new Position( root, [ 3, 3, 0 ] ),
  1769. 2,
  1770. new Position( root, [ 4, 1, 0 ] ),
  1771. baseVersion
  1772. );
  1773. const transOp = transform( op, transformBy );
  1774. expected.targetPosition = expected.targetPosition.getShiftedBy( -2 );
  1775. expect( transOp.length ).to.equal( 1 );
  1776. expectOperation( transOp[ 0 ], expected );
  1777. } );
  1778. it( 'range offset after transforming target offset: no operation update', () => {
  1779. const transformBy = new MoveOperation(
  1780. new Position( root, [ 3, 3, 5 ] ),
  1781. 2,
  1782. new Position( root, [ 4, 1, 0 ] ),
  1783. baseVersion
  1784. );
  1785. const transOp = transform( op, transformBy );
  1786. expect( transOp.length ).to.equal( 1 );
  1787. expectOperation( transOp[ 0 ], expected );
  1788. } );
  1789. it( 'target before node from transforming target path: increment index on target path', () => {
  1790. const transformBy = new MoveOperation(
  1791. new Position( root, [ 4, 1, 0 ] ),
  1792. 2,
  1793. new Position( root, [ 3, 1 ] ),
  1794. baseVersion
  1795. );
  1796. const transOp = transform( op, transformBy );
  1797. expected.targetPosition = getPositionMovedInPath( expected.targetPosition, 1, 2 );
  1798. expect( transOp.length ).to.equal( 1 );
  1799. expectOperation( transOp[ 0 ], expected );
  1800. } );
  1801. it( 'target after node from transforming target path: no operation update', () => {
  1802. const transformBy = new MoveOperation(
  1803. new Position( root, [ 4, 1, 0 ] ),
  1804. 2,
  1805. new Position( root, [ 3, 5 ] ),
  1806. baseVersion
  1807. );
  1808. const transOp = transform( op, transformBy );
  1809. expect( transOp.length ).to.equal( 1 );
  1810. expectOperation( transOp[ 0 ], expected );
  1811. } );
  1812. it( 'range before node from transforming target path: decrement index on target path', () => {
  1813. const transformBy = new MoveOperation(
  1814. new Position( root, [ 3, 0 ] ),
  1815. 2,
  1816. new Position( root, [ 4, 1, 0 ] ),
  1817. baseVersion
  1818. );
  1819. const transOp = transform( op, transformBy );
  1820. expected.targetPosition = getPositionMovedInPath( expected.targetPosition, 1, -2 );
  1821. expect( transOp.length ).to.equal( 1 );
  1822. expectOperation( transOp[ 0 ], expected );
  1823. } );
  1824. it( 'range after node from transforming target path: no operation update', () => {
  1825. const transformBy = new MoveOperation(
  1826. new Position( root, [ 3, 5 ] ),
  1827. 2,
  1828. new Position( root, [ 4, 1, 0 ] ),
  1829. baseVersion
  1830. );
  1831. const transOp = transform( op, transformBy );
  1832. expect( transOp.length ).to.equal( 1 );
  1833. expectOperation( transOp[ 0 ], expected );
  1834. } );
  1835. it( 'target inside transforming move range: split into two operations', () => {
  1836. const transformBy = new MoveOperation(
  1837. new Position( root, [ 4, 1, 0 ] ),
  1838. 2,
  1839. new Position( root, [ 2, 2, 5 ] ),
  1840. baseVersion
  1841. );
  1842. const transOp = transform( op, transformBy );
  1843. expect( transOp.length ).to.equal( 2 );
  1844. expected.howMany = 1;
  1845. expectOperation( transOp[ 0 ], expected );
  1846. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 6 ] );
  1847. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  1848. expected.baseVersion++;
  1849. expectOperation( transOp[ 1 ], expected );
  1850. } );
  1851. it( 'target at start boundary of transforming move range: increment source offset', () => {
  1852. const transformBy = new MoveOperation(
  1853. new Position( root, [ 4, 1, 0 ] ),
  1854. 2,
  1855. new Position( root, [ 2, 2, 4 ] ),
  1856. baseVersion
  1857. );
  1858. const transOp = transform( op, transformBy );
  1859. expect( transOp.length ).to.equal( 1 );
  1860. expected.sourcePosition = expected.sourcePosition.getShiftedTo( 6 );
  1861. expectOperation( transOp[ 0 ], expected );
  1862. } );
  1863. it( 'target at start boundary of transforming move range (sticky move): expand move range', () => {
  1864. const transformBy = new MoveOperation(
  1865. new Position( root, [ 4, 1, 0 ] ),
  1866. 2,
  1867. new Position( root, [ 2, 2, 4 ] ),
  1868. baseVersion
  1869. );
  1870. op.isSticky = true;
  1871. const transOp = transform( op, transformBy );
  1872. expect( transOp.length ).to.equal( 1 );
  1873. expected.howMany = 4;
  1874. expectOperation( transOp[ 0 ], expected );
  1875. } );
  1876. it( 'target at end boundary of transforming move range: no operation update', () => {
  1877. const transformBy = new MoveOperation(
  1878. new Position( root, [ 4, 1, 0 ] ),
  1879. 2,
  1880. new Position( root, [ 2, 2, 6 ] ),
  1881. baseVersion
  1882. );
  1883. const transOp = transform( op, transformBy );
  1884. expect( transOp.length ).to.equal( 1 );
  1885. expectOperation( transOp[ 0 ], expected );
  1886. } );
  1887. it( 'target at end boundary of transforming move range (sticky move): expand move range', () => {
  1888. const transformBy = new MoveOperation(
  1889. new Position( root, [ 4, 1, 0 ] ),
  1890. 2,
  1891. new Position( root, [ 2, 2, 6 ] ),
  1892. baseVersion
  1893. );
  1894. op.isSticky = true;
  1895. const transOp = transform( op, transformBy );
  1896. expect( transOp.length ).to.equal( 1 );
  1897. expected.howMany = 4;
  1898. expectOperation( transOp[ 0 ], expected );
  1899. } );
  1900. it( 'target inside a node from transforming range: no operation update', () => {
  1901. const transformBy = new MoveOperation(
  1902. new Position( root, [ 4, 1, 0 ] ),
  1903. 2,
  1904. new Position( root, [ 2, 2, 5, 1 ] ),
  1905. baseVersion
  1906. );
  1907. const transOp = transform( op, transformBy );
  1908. expect( transOp.length ).to.equal( 1 );
  1909. expectOperation( transOp[ 0 ], expected );
  1910. } );
  1911. it( 'range has node that contains transforming range: update range path', () => {
  1912. const transformBy = new MoveOperation(
  1913. new Position( root, [ 2, 1 ] ),
  1914. 3,
  1915. new Position( root, [ 4, 2 ] ),
  1916. baseVersion
  1917. );
  1918. const transOp = transform( op, transformBy );
  1919. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 4, 3, 4 ] );
  1920. expect( transOp.length ).to.equal( 1 );
  1921. expectOperation( transOp[ 0 ], expected );
  1922. } );
  1923. it( 'range has node that contains transforming target: update target path', () => {
  1924. const transformBy = new MoveOperation(
  1925. new Position( root, [ 3, 2 ] ),
  1926. 3,
  1927. new Position( root, [ 0, 1 ] ),
  1928. baseVersion
  1929. );
  1930. const transOp = transform( op, transformBy );
  1931. expected.targetPosition = new Position( expected.targetPosition.root, [ 0, 2, 3 ] );
  1932. expect( transOp.length ).to.equal( 1 );
  1933. expectOperation( transOp[ 0 ], expected );
  1934. } );
  1935. it( 'target inside a node from transforming range and vice versa: reverse transform-by operation', () => {
  1936. const transformBy = new MoveOperation(
  1937. new Position( root, [ 3, 2 ] ),
  1938. 3,
  1939. new Position( root, [ 2, 2, 5, 0 ] ),
  1940. baseVersion
  1941. );
  1942. const transOp = transform( op, transformBy );
  1943. const reversed = transformBy.getReversed();
  1944. expected.sourcePosition = reversed.sourcePosition;
  1945. expected.targetPosition = reversed.targetPosition;
  1946. expected.howMany = reversed.howMany;
  1947. expect( transOp.length ).to.equal( 1 );
  1948. expectOperation( transOp[ 0 ], expected );
  1949. } );
  1950. it( 'range is same as transforming range and is important: convert to NoOperation', () => {
  1951. const transformBy = new MoveOperation(
  1952. op.sourcePosition,
  1953. op.howMany,
  1954. new Position( root, [ 4, 1, 0 ] ),
  1955. baseVersion
  1956. );
  1957. const transOp = transform( op, transformBy );
  1958. expect( transOp.length ).to.equal( 1 );
  1959. expectOperation( transOp[ 0 ], {
  1960. type: NoOperation,
  1961. baseVersion: baseVersion + 1
  1962. } );
  1963. } );
  1964. it( 'range is same as transforming range and is less important: update range path', () => {
  1965. const transformBy = new MoveOperation(
  1966. op.sourcePosition,
  1967. op.howMany,
  1968. new Position( root, [ 4, 1, 0 ] ),
  1969. baseVersion
  1970. );
  1971. const transOp = transform( op, transformBy, { isStrong: true } );
  1972. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 4, 1, 0 ] );
  1973. expect( transOp.length ).to.equal( 1 );
  1974. expectOperation( transOp[ 0 ], expected );
  1975. } );
  1976. it( 'range contains transforming range and is important: convert to NoOperation', () => {
  1977. const transformBy = new MoveOperation(
  1978. new Position( root, [ 2, 2, 3 ] ),
  1979. 4,
  1980. new Position( root, [ 4, 1, 0 ] ),
  1981. baseVersion
  1982. );
  1983. const transOp = transform( op, transformBy );
  1984. expect( transOp.length ).to.equal( 1 );
  1985. expectOperation( transOp[ 0 ], {
  1986. type: NoOperation,
  1987. baseVersion: baseVersion + 1
  1988. } );
  1989. } );
  1990. it( 'range contains transforming range and is less important: update range path', () => {
  1991. const transformBy = new MoveOperation(
  1992. new Position( root, [ 2, 2, 3 ] ),
  1993. 4,
  1994. new Position( root, [ 4, 1, 0 ] ),
  1995. baseVersion
  1996. );
  1997. const transOp = transform( op, transformBy, { isStrong: true } );
  1998. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 4, 1, 1 ] );
  1999. expect( transOp.length ).to.equal( 1 );
  2000. expectOperation( transOp[ 0 ], expected );
  2001. } );
  2002. it( 'range contains transforming range and target and is important: update range path and target', () => {
  2003. op.targetPosition = new Position( op.targetPosition.root, [ 2, 2, 7 ] );
  2004. const transformBy = new MoveOperation(
  2005. new Position( root, [ 2, 2, 3 ] ),
  2006. 5,
  2007. new Position( root, [ 4, 1, 0 ] ),
  2008. baseVersion
  2009. );
  2010. const transOp = transform( op, transformBy );
  2011. expect( transOp.length ).to.equal( 1 );
  2012. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 4, 1, 1 ] );
  2013. expected.targetPosition = new Position( expected.targetPosition.root, [ 4, 1, 4 ] );
  2014. expectOperation( transOp[ 0 ], expected );
  2015. } );
  2016. it( 'range contains transforming range and target and is less important: update range path and target', () => {
  2017. op.targetPosition = new Position( op.targetPosition.root, [ 2, 2, 7 ] );
  2018. const transformBy = new MoveOperation(
  2019. new Position( root, [ 2, 2, 3 ] ),
  2020. 5,
  2021. new Position( root, [ 4, 1, 0 ] ),
  2022. baseVersion
  2023. );
  2024. const transOp = transform( op, transformBy, { isStrong: true } );
  2025. expect( transOp.length ).to.equal( 1 );
  2026. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 4, 1, 1 ] );
  2027. expected.targetPosition = new Position( expected.targetPosition.root, [ 4, 1, 4 ] );
  2028. expectOperation( transOp[ 0 ], expected );
  2029. } );
  2030. it( 'range intersects on left side of transforming range and is important: shrink range', () => {
  2031. const transformBy = new MoveOperation(
  2032. new Position( root, [ 2, 2, 3 ] ),
  2033. 2,
  2034. new Position( root, [ 4, 1, 0 ] ),
  2035. baseVersion
  2036. );
  2037. const transOp = transform( op, transformBy );
  2038. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 3 ] );
  2039. expected.howMany = 1;
  2040. expect( transOp.length ).to.equal( 1 );
  2041. expectOperation( transOp[ 0 ], expected );
  2042. } );
  2043. it( 'range intersects on left side of transforming range and is less important: split into two operations', () => {
  2044. // Get more test cases and better code coverage
  2045. const otherRoot = new RootElement( null );
  2046. const transformBy = new MoveOperation(
  2047. new Position( root, [ 2, 2, 3 ] ),
  2048. 2,
  2049. new Position( otherRoot, [ 4, 1, 0 ] ),
  2050. baseVersion
  2051. );
  2052. const transOp = transform( op, transformBy, { isStrong: true } );
  2053. expect( transOp.length ).to.equal( 2 );
  2054. expected.sourcePosition = new Position( otherRoot, [ 4, 1, 1 ] );
  2055. expected.howMany = 1;
  2056. expectOperation( transOp[ 0 ], expected );
  2057. expected.sourcePosition = new Position( root, [ 2, 2, 3 ] );
  2058. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  2059. expected.baseVersion++;
  2060. expectOperation( transOp[ 1 ], expected );
  2061. } );
  2062. it( 'range intersects on right side of transforming range and is important: shrink range', () => {
  2063. const transformBy = new MoveOperation(
  2064. new Position( root, [ 2, 2, 5 ] ),
  2065. 2,
  2066. new Position( root, [ 4, 1, 0 ] ),
  2067. baseVersion
  2068. );
  2069. const transOp = transform( op, transformBy );
  2070. expected.howMany = 1;
  2071. expect( transOp.length ).to.equal( 1 );
  2072. expectOperation( transOp[ 0 ], expected );
  2073. } );
  2074. it( 'range intersects on right side of transforming range and is less important: split into two operations', () => {
  2075. const transformBy = new MoveOperation(
  2076. new Position( root, [ 2, 2, 5 ] ),
  2077. 2,
  2078. new Position( root, [ 4, 1, 0 ] ),
  2079. baseVersion
  2080. );
  2081. const transOp = transform( op, transformBy, { isStrong: true } );
  2082. expect( transOp.length ).to.equal( 2 );
  2083. expected.sourcePosition = sourcePosition;
  2084. expected.howMany = 1;
  2085. expectOperation( transOp[ 0 ], expected );
  2086. expected.sourcePosition = new Position( root, [ 4, 1, 0 ] );
  2087. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  2088. expected.baseVersion++;
  2089. expectOperation( transOp[ 1 ], expected );
  2090. } );
  2091. it( 'range intersects on left side, target inside transforming range and is important: split into two operations', () => {
  2092. op.howMany = 4;
  2093. const transformBy = new MoveOperation(
  2094. new Position( root, [ 2, 2, 3 ] ),
  2095. 2,
  2096. new Position( root, [ 2, 2, 6 ] ),
  2097. baseVersion
  2098. );
  2099. const transOp = transform( op, transformBy );
  2100. expect( transOp.length ).to.equal( 2 );
  2101. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 3 ] );
  2102. expected.howMany = 1;
  2103. expectOperation( transOp[ 0 ], expected );
  2104. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 5 ] );
  2105. expected.howMany = 2;
  2106. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  2107. expected.baseVersion++;
  2108. expectOperation( transOp[ 1 ], expected );
  2109. } );
  2110. it( 'range intersects on left side, target inside transforming range and is less important: split into two operations', () => {
  2111. op.howMany = 4;
  2112. const transformBy = new MoveOperation(
  2113. new Position( root, [ 2, 2, 3 ] ),
  2114. 2,
  2115. new Position( root, [ 2, 2, 6 ] ),
  2116. baseVersion
  2117. );
  2118. const transOp = transform( op, transformBy, { isStrong: true } );
  2119. expect( transOp.length ).to.equal( 2 );
  2120. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 3 ] );
  2121. expected.howMany = 1;
  2122. expectOperation( transOp[ 0 ], expected );
  2123. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 5 ] );
  2124. expected.howMany = 2;
  2125. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  2126. expected.baseVersion++;
  2127. expectOperation( transOp[ 1 ], expected );
  2128. } );
  2129. it( 'range intersects on right side of transforming range and is important: shrink range', () => {
  2130. const transformBy = new MoveOperation(
  2131. new Position( root, [ 2, 2, 5 ] ),
  2132. 2,
  2133. new Position( root, [ 4, 1, 0 ] ),
  2134. baseVersion
  2135. );
  2136. const transOp = transform( op, transformBy );
  2137. expected.howMany = 1;
  2138. expect( transOp.length ).to.equal( 1 );
  2139. expectOperation( transOp[ 0 ], expected );
  2140. } );
  2141. it( 'range intersects on right side of transforming range and is less important: split into two operations', () => {
  2142. const transformBy = new MoveOperation(
  2143. new Position( root, [ 2, 2, 5 ] ),
  2144. 2,
  2145. new Position( root, [ 4, 1, 0 ] ),
  2146. baseVersion
  2147. );
  2148. const transOp = transform( op, transformBy, { isStrong: true } );
  2149. expect( transOp.length ).to.equal( 2 );
  2150. expected.sourcePosition = sourcePosition;
  2151. expected.howMany = 1;
  2152. expectOperation( transOp[ 0 ], expected );
  2153. expected.sourcePosition = new Position( root, [ 4, 1, 0 ] );
  2154. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  2155. expected.baseVersion++;
  2156. expectOperation( transOp[ 1 ], expected );
  2157. } );
  2158. it( 'range intersects, target inside transforming range and is important: split into two operations', () => {
  2159. op.targetPosition = new Position( op.targetPosition.root, [ 2, 2, 7 ] );
  2160. const transformBy = new MoveOperation(
  2161. new Position( root, [ 2, 2, 5 ] ),
  2162. 4,
  2163. new Position( root, [ 4, 1, 0 ] ),
  2164. baseVersion
  2165. );
  2166. const transOp = transform( op, transformBy, { isStrong: true } );
  2167. expect( transOp.length ).to.equal( 2 );
  2168. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 4 ] );
  2169. expected.targetPosition = new Position( expected.targetPosition.root, [ 4, 1, 2 ] );
  2170. expected.howMany = 1;
  2171. expectOperation( transOp[ 0 ], expected );
  2172. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 4, 1, 0 ] );
  2173. expected.targetPosition = new Position( expected.targetPosition.root, [ 4, 1, 3 ] );
  2174. expected.baseVersion++;
  2175. expectOperation( transOp[ 1 ], expected );
  2176. } );
  2177. it( 'range intersects, target inside transforming range and is less important: shrink range', () => {
  2178. op.targetPosition = new Position( op.targetPosition.root, [ 2, 2, 7 ] );
  2179. const transformBy = new MoveOperation(
  2180. new Position( root, [ 2, 2, 5 ] ),
  2181. 4,
  2182. new Position( root, [ 4, 1, 0 ] ),
  2183. baseVersion
  2184. );
  2185. const transOp = transform( op, transformBy );
  2186. expect( transOp.length ).to.equal( 1 );
  2187. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 4 ] );
  2188. expected.targetPosition = new Position( expected.targetPosition.root, [ 4, 1, 2 ] );
  2189. expected.howMany = 1;
  2190. expectOperation( transOp[ 0 ], expected );
  2191. } );
  2192. it( 'range inside transforming range and is important: split into two operations', () => {
  2193. op.howMany = 4;
  2194. const transformBy = new MoveOperation(
  2195. new Position( root, [ 2, 2, 5 ] ),
  2196. 2,
  2197. new Position( root, [ 4, 1, 0 ] ),
  2198. baseVersion
  2199. );
  2200. const transOp = transform( op, transformBy );
  2201. expect( transOp.length ).to.equal( 2 );
  2202. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 4 ] );
  2203. expected.howMany = 1;
  2204. expectOperation( transOp[ 0 ], expected );
  2205. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  2206. expected.baseVersion++;
  2207. expectOperation( transOp[ 1 ], expected );
  2208. } );
  2209. it( 'range inside transforming range and is less important: split into three operations', () => {
  2210. op.howMany = 4;
  2211. const transformBy = new MoveOperation(
  2212. new Position( root, [ 2, 2, 5 ] ),
  2213. 2,
  2214. new Position( root, [ 4, 1, 0 ] ),
  2215. baseVersion
  2216. );
  2217. const transOp = transform( op, transformBy, { isStrong: true } );
  2218. expect( transOp.length ).to.equal( 3 );
  2219. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 4 ] );
  2220. expected.howMany = 1;
  2221. expectOperation( transOp[ 0 ], expected );
  2222. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 4, 1, 0 ] );
  2223. expected.targetPosition = targetPosition.getShiftedBy( 1 );
  2224. expected.howMany = 2;
  2225. expected.baseVersion++;
  2226. expectOperation( transOp[ 1 ], expected );
  2227. expected.sourcePosition = new Position( expected.sourcePosition.root, [ 2, 2, 4 ] );
  2228. expected.targetPosition = targetPosition.getShiftedBy( 3 );
  2229. expected.howMany = 1;
  2230. expected.baseVersion++;
  2231. expectOperation( transOp[ 2 ], expected );
  2232. } );
  2233. it( 'range and target inside transforming range and is important: no operation update', () => {
  2234. op.howMany = 6;
  2235. const transformBy = new MoveOperation(
  2236. new Position( root, [ 2, 2, 5 ] ),
  2237. 2,
  2238. new Position( root, [ 2, 2, 9 ] ),
  2239. baseVersion
  2240. );
  2241. const transOp = transform( op, transformBy );
  2242. expect( transOp.length ).to.equal( 1 );
  2243. expected.howMany = 6;
  2244. expectOperation( transOp[ 0 ], expected );
  2245. } );
  2246. it( 'range and target inside transforming range and is less important: no operation update', () => {
  2247. op.howMany = 6;
  2248. const transformBy = new MoveOperation(
  2249. new Position( root, [ 2, 2, 5 ] ),
  2250. 2,
  2251. new Position( root, [ 2, 2, 9 ] ),
  2252. baseVersion
  2253. );
  2254. const transOp = transform( op, transformBy, { isStrong: true } );
  2255. expect( transOp.length ).to.equal( 1 );
  2256. expected.howMany = 6;
  2257. expectOperation( transOp[ 0 ], expected );
  2258. } );
  2259. } );
  2260. describe( 'by RemoveOperation', () => {
  2261. let transformBy;
  2262. beforeEach( () => {
  2263. transformBy = new RemoveOperation(
  2264. Position.createFromPosition( op.sourcePosition ),
  2265. op.howMany,
  2266. new Position( doc.graveyard, [ 0 ] ),
  2267. baseVersion
  2268. );
  2269. } );
  2270. it( 'should skip context.isStrong and be less important than RemoveOperation', () => {
  2271. const transOp = transform( op, transformBy, { isStrong: true } );
  2272. expect( transOp.length ).to.equal( 1 );
  2273. expectOperation( transOp[ 0 ], {
  2274. type: NoOperation,
  2275. baseVersion: baseVersion + 1
  2276. } );
  2277. } );
  2278. it( 'should use context.isStrong if forceWeakRemove flag is used', () => {
  2279. const transOp = transform( op, transformBy, { isStrong: true, forceWeakRemove: true } );
  2280. expect( transOp.length ).to.equal( 1 );
  2281. expected.sourcePosition = transformBy.targetPosition;
  2282. expectOperation( transOp[ 0 ], expected );
  2283. } );
  2284. } );
  2285. describe( 'by NoOperation', () => {
  2286. it( 'no operation update', () => {
  2287. const transformBy = new NoOperation( baseVersion );
  2288. const transOp = transform( op, transformBy );
  2289. expect( transOp.length ).to.equal( 1 );
  2290. expectOperation( transOp[ 0 ], expected );
  2291. } );
  2292. } );
  2293. describe( 'by RenameOperation', () => {
  2294. it( 'no position update', () => {
  2295. const transformBy = new RenameOperation( new Position( root, [ 2, 2, 4 ] ), 'oldName', 'newName', baseVersion );
  2296. const transOp = transform( op, transformBy );
  2297. expect( transOp.length ).to.equal( 1 );
  2298. expectOperation( transOp[ 0 ], expected );
  2299. } );
  2300. } );
  2301. describe( 'by MarkerOperation', () => {
  2302. it( 'no position update', () => {
  2303. const newRange = new Range( new Position( root, [ 2, 2, 3 ] ), new Position( root, [ 2, 2, 8 ] ) );
  2304. const transformBy = new MarkerOperation( 'name', null, newRange, doc.markers, baseVersion );
  2305. const transOp = transform( op, transformBy );
  2306. expect( transOp.length ).to.equal( 1 );
  2307. expectOperation( transOp[ 0 ], expected );
  2308. } );
  2309. } );
  2310. } );
  2311. describe( 'RemoveOperation', () => {
  2312. describe( 'by MoveOperation', () => {
  2313. it( 'should force removing content even if was less important', () => {
  2314. const op = new RemoveOperation( new Position( root, [ 8 ] ), 2, new Position( doc.graveyard, [ 0 ] ), baseVersion );
  2315. const targetPosition = Position.createFromPosition( op.targetPosition );
  2316. const transformBy = new MoveOperation( new Position( root, [ 8 ] ), 2, new Position( root, [ 1 ] ), baseVersion );
  2317. const sourcePosition = Position.createFromPosition( transformBy.targetPosition );
  2318. const transOp = transform( op, transformBy );
  2319. expect( transOp.length ).to.equal( 1 );
  2320. expectOperation( transOp[ 0 ], {
  2321. type: RemoveOperation,
  2322. howMany: 2,
  2323. sourcePosition,
  2324. targetPosition,
  2325. baseVersion: baseVersion + 1
  2326. } );
  2327. } );
  2328. } );
  2329. } );
  2330. describe( 'NoOperation', () => {
  2331. beforeEach( () => {
  2332. op = new NoOperation( baseVersion );
  2333. expected = {
  2334. type: NoOperation,
  2335. baseVersion: baseVersion + 1
  2336. };
  2337. } );
  2338. describe( 'by InsertOperation', () => {
  2339. it( 'no operation update', () => {
  2340. const transformBy = new InsertOperation(
  2341. new Position( root, [ 0 ] ),
  2342. 'a',
  2343. baseVersion
  2344. );
  2345. const transOp = transform( op, transformBy );
  2346. expect( transOp.length ).to.equal( 1 );
  2347. expectOperation( transOp[ 0 ], expected );
  2348. } );
  2349. } );
  2350. describe( 'by AttributeOperation', () => {
  2351. it( 'no operation update', () => {
  2352. const transformBy = new AttributeOperation(
  2353. new Range(
  2354. new Position( root, [ 0 ] ),
  2355. new Position( root, [ 1 ] )
  2356. ),
  2357. 'foo',
  2358. 'bar',
  2359. 'xyz',
  2360. baseVersion
  2361. );
  2362. const transOp = transform( op, transformBy );
  2363. expect( transOp.length ).to.equal( 1 );
  2364. expectOperation( transOp[ 0 ], expected );
  2365. } );
  2366. } );
  2367. describe( 'by RootAttributeOperation', () => {
  2368. it( 'no operation update', () => {
  2369. const transformBy = new RootAttributeOperation(
  2370. root,
  2371. 'foo',
  2372. null,
  2373. 'bar',
  2374. baseVersion
  2375. );
  2376. const transOp = transform( op, transformBy );
  2377. expect( transOp.length ).to.equal( 1 );
  2378. expectOperation( transOp[ 0 ], expected );
  2379. } );
  2380. } );
  2381. describe( 'by MoveOperation', () => {
  2382. it( 'no operation update', () => {
  2383. const transformBy = new MoveOperation(
  2384. new Position( root, [ 0 ] ),
  2385. 2,
  2386. new Position( root, [ 1 ] ),
  2387. baseVersion
  2388. );
  2389. const transOp = transform( op, transformBy );
  2390. expect( transOp.length ).to.equal( 1 );
  2391. expectOperation( transOp[ 0 ], expected );
  2392. } );
  2393. } );
  2394. describe( 'by NoOperation', () => {
  2395. it( 'no operation update', () => {
  2396. const transformBy = new NoOperation( baseVersion );
  2397. const transOp = transform( op, transformBy );
  2398. expect( transOp.length ).to.equal( 1 );
  2399. expectOperation( transOp[ 0 ], expected );
  2400. } );
  2401. } );
  2402. describe( 'by RenameOperation', () => {
  2403. it( 'no position update', () => {
  2404. const transformBy = new RenameOperation( new Position( root, [ 0, 2, 0 ] ), 'oldName', 'newName', baseVersion );
  2405. const transOp = transform( op, transformBy );
  2406. expect( transOp.length ).to.equal( 1 );
  2407. expectOperation( transOp[ 0 ], expected );
  2408. } );
  2409. } );
  2410. describe( 'by MarkerOperation', () => {
  2411. it( 'no position update', () => {
  2412. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 8 ] ) );
  2413. const transformBy = new MarkerOperation( 'name', null, newRange, doc.markers, baseVersion );
  2414. const transOp = transform( op, transformBy );
  2415. expect( transOp.length ).to.equal( 1 );
  2416. expectOperation( transOp[ 0 ], expected );
  2417. } );
  2418. } );
  2419. } );
  2420. describe( 'RenameOperation', () => {
  2421. beforeEach( () => {
  2422. const position = new Position( root, [ 0, 2, 2 ] );
  2423. op = new RenameOperation( position, 'oldName', 'newName', baseVersion );
  2424. expected = {
  2425. position,
  2426. oldName: 'oldName',
  2427. newName: 'newName',
  2428. baseVersion: baseVersion + 1
  2429. };
  2430. } );
  2431. describe( 'by InsertOperation', () => {
  2432. it( 'target before renamed element: offset update', () => {
  2433. const transformBy = new InsertOperation(
  2434. new Position( root, [ 0, 2, 1 ] ),
  2435. [ nodeA, nodeB ],
  2436. baseVersion
  2437. );
  2438. const transOp = transform( op, transformBy );
  2439. expect( transOp.length ).to.equal( 1 );
  2440. expected.position = expected.position.getShiftedTo( 4 );
  2441. expectOperation( transOp[ 0 ], expected );
  2442. } );
  2443. it( 'target after renamed element: no change', () => {
  2444. const transformBy = new InsertOperation(
  2445. new Position( root, [ 0, 2, 3 ] ),
  2446. [ nodeA, nodeB ],
  2447. baseVersion
  2448. );
  2449. const transOp = transform( op, transformBy );
  2450. expect( transOp.length ).to.equal( 1 );
  2451. expectOperation( transOp[ 0 ], expected );
  2452. } );
  2453. it( 'target before a node on path to renamed element: path update', () => {
  2454. const transformBy = new InsertOperation(
  2455. new Position( root, [ 0, 1 ] ),
  2456. [ nodeA, nodeB ],
  2457. baseVersion
  2458. );
  2459. const transOp = transform( op, transformBy );
  2460. expect( transOp.length ).to.equal( 1 );
  2461. expected.position = new Position( expected.position.root, [ 0, 4, 2 ] );
  2462. expectOperation( transOp[ 0 ], expected );
  2463. } );
  2464. it( 'target after a node on path to renamed element: no change', () => {
  2465. const transformBy = new InsertOperation(
  2466. new Position( root, [ 0, 3 ] ),
  2467. [ nodeA, nodeB ],
  2468. baseVersion
  2469. );
  2470. const transOp = transform( op, transformBy );
  2471. expect( transOp.length ).to.equal( 1 );
  2472. expectOperation( transOp[ 0 ], expected );
  2473. } );
  2474. } );
  2475. describe( 'by AttributeOperation', () => {
  2476. it( 'no operation update', () => {
  2477. const transformBy = new AttributeOperation(
  2478. new Range(
  2479. new Position( root, [ 0, 2, 1 ] ),
  2480. new Position( root, [ 1, 3 ] )
  2481. ),
  2482. 'foo',
  2483. 'bar',
  2484. 'xyz',
  2485. baseVersion
  2486. );
  2487. const transOp = transform( op, transformBy );
  2488. expect( transOp.length ).to.equal( 1 );
  2489. expectOperation( transOp[ 0 ], expected );
  2490. } );
  2491. } );
  2492. describe( 'by RootAttributeOperation', () => {
  2493. it( 'no operation update', () => {
  2494. const transformBy = new RootAttributeOperation(
  2495. root,
  2496. 'foo',
  2497. null,
  2498. 'bar',
  2499. baseVersion
  2500. );
  2501. const transOp = transform( op, transformBy );
  2502. expect( transOp.length ).to.equal( 1 );
  2503. expectOperation( transOp[ 0 ], expected );
  2504. } );
  2505. } );
  2506. describe( 'by MarkerOperation', () => {
  2507. it( 'no operation update', () => {
  2508. const newRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 8 ] ) );
  2509. const transformBy = new MarkerOperation( 'name', null, newRange, doc.markers, baseVersion );
  2510. const transOp = transform( op, transformBy );
  2511. expect( transOp.length ).to.equal( 1 );
  2512. expectOperation( transOp[ 0 ], expected );
  2513. } );
  2514. } );
  2515. describe( 'by RenameOperation', () => {
  2516. it( 'different element: no change', () => {
  2517. const transformBy = new RenameOperation(
  2518. new Position( root, [ 0, 2, 1 ] ),
  2519. 'foo',
  2520. 'bar',
  2521. baseVersion
  2522. );
  2523. const transOp = transform( op, transformBy );
  2524. expect( transOp.length ).to.equal( 1 );
  2525. expectOperation( transOp[ 0 ], expected );
  2526. } );
  2527. it( 'same element and is important: convert to NoOperation', () => {
  2528. const transformBy = new RenameOperation(
  2529. new Position( root, [ 0, 2, 2 ] ),
  2530. 'oldName',
  2531. 'otherName',
  2532. baseVersion
  2533. );
  2534. const transOp = transform( op, transformBy );
  2535. expect( transOp.length ).to.equal( 1 );
  2536. expectOperation( transOp[ 0 ], {
  2537. type: NoOperation,
  2538. baseVersion: baseVersion + 1
  2539. } );
  2540. } );
  2541. it( 'same element and is not important: change old name to new name', () => {
  2542. const transformBy = new RenameOperation(
  2543. new Position( root, [ 0, 2, 2 ] ),
  2544. 'oldName',
  2545. 'otherName',
  2546. baseVersion
  2547. );
  2548. const transOp = transform( op, transformBy, { isStrong: true } );
  2549. expect( transOp.length ).to.equal( 1 );
  2550. expected.oldName = 'otherName';
  2551. expectOperation( transOp[ 0 ], expected );
  2552. } );
  2553. } );
  2554. describe( 'by MoveOperation', () => {
  2555. it( 'moved range before renamed element: update offset', () => {
  2556. const transformBy = new MoveOperation(
  2557. new Position( root, [ 0, 2, 0 ] ),
  2558. 2,
  2559. new Position( root, [ 2, 5 ] ),
  2560. baseVersion
  2561. );
  2562. const transOp = transform( op, transformBy );
  2563. expect( transOp.length ).to.equal( 1 );
  2564. expected.position = expected.position.getShiftedTo( 0 );
  2565. expectOperation( transOp[ 0 ], expected );
  2566. } );
  2567. it( 'moved range before an element on path to renamed element: update path', () => {
  2568. const transformBy = new MoveOperation(
  2569. new Position( root, [ 0, 0 ] ),
  2570. 2,
  2571. new Position( root, [ 2, 5 ] ),
  2572. baseVersion
  2573. );
  2574. const transOp = transform( op, transformBy );
  2575. expect( transOp.length ).to.equal( 1 );
  2576. expected.position = new Position( expected.position.root, [ 0, 0, 2 ] );
  2577. expectOperation( transOp[ 0 ], expected );
  2578. } );
  2579. it( 'moved range contains renamed element: update path', () => {
  2580. const transformBy = new MoveOperation(
  2581. new Position( root, [ 0, 2, 1 ] ),
  2582. 3,
  2583. new Position( root, [ 2, 5 ] ),
  2584. baseVersion
  2585. );
  2586. const transOp = transform( op, transformBy );
  2587. expect( transOp.length ).to.equal( 1 );
  2588. expected.position = new Position( expected.position.root, [ 2, 6 ] );
  2589. expectOperation( transOp[ 0 ], expected );
  2590. } );
  2591. it( 'moved range contains renamed element parent: updated path', () => {
  2592. const transformBy = new MoveOperation(
  2593. new Position( root, [ 0, 1 ] ),
  2594. 3,
  2595. new Position( root, [ 2, 5 ] ),
  2596. baseVersion
  2597. );
  2598. const transOp = transform( op, transformBy );
  2599. expect( transOp.length ).to.equal( 1 );
  2600. expected.position = new Position( expected.position.root, [ 2, 6, 2 ] );
  2601. expectOperation( transOp[ 0 ], expected );
  2602. } );
  2603. it( 'move target before renamed element: update offset', () => {
  2604. const transformBy = new MoveOperation(
  2605. new Position( root, [ 2, 5 ] ),
  2606. 2,
  2607. new Position( root, [ 0, 2, 1 ] ),
  2608. baseVersion
  2609. );
  2610. const transOp = transform( op, transformBy );
  2611. expect( transOp.length ).to.equal( 1 );
  2612. expected.position = expected.position.getShiftedTo( 4 );
  2613. expectOperation( transOp[ 0 ], expected );
  2614. } );
  2615. it( 'move target before an element on path to renamed element: update path', () => {
  2616. const transformBy = new MoveOperation(
  2617. new Position( root, [ 0, 2, 0 ] ),
  2618. 2,
  2619. new Position( root, [ 2, 5 ] ),
  2620. baseVersion
  2621. );
  2622. const transOp = transform( op, transformBy );
  2623. expect( transOp.length ).to.equal( 1 );
  2624. expected.position = expected.position.getShiftedTo( 0 );
  2625. expectOperation( transOp[ 0 ], expected );
  2626. } );
  2627. } );
  2628. } );
  2629. describe( 'MarkerOperation', () => {
  2630. let oldRange, newRange;
  2631. beforeEach( () => {
  2632. oldRange = Range.createFromParentsAndOffsets( root, 1, root, 4 );
  2633. newRange = Range.createFromParentsAndOffsets( root, 10, root, 12 );
  2634. op = new MarkerOperation( 'name', oldRange, newRange, doc.markers, baseVersion );
  2635. expected = {
  2636. name: 'name',
  2637. oldRange,
  2638. newRange,
  2639. baseVersion: baseVersion + 1
  2640. };
  2641. } );
  2642. describe( 'by InsertOperation', () => {
  2643. it( 'insert position affecting oldRange: update oldRange', () => {
  2644. // Just CC things.
  2645. op.newRange = null;
  2646. const transformBy = new InsertOperation( Position.createAt( root, 0 ), [ nodeA, nodeB ], baseVersion );
  2647. const transOp = transform( op, transformBy );
  2648. expected.newRange = null;
  2649. expected.oldRange = new Range(
  2650. expected.oldRange.start.getShiftedTo( 3 ),
  2651. expected.oldRange.end.getShiftedTo( 6 )
  2652. );
  2653. expect( transOp.length ).to.equal( 1 );
  2654. expectOperation( transOp[ 0 ], expected );
  2655. } );
  2656. it( 'insert position affecting newRange: update newRange', () => {
  2657. // Just CC things.
  2658. op.oldRange = null;
  2659. const transformBy = new InsertOperation( Position.createAt( root, 8 ), [ nodeA, nodeB ], baseVersion );
  2660. const transOp = transform( op, transformBy );
  2661. expected.oldRange = null;
  2662. expected.newRange = new Range(
  2663. expected.newRange.start.getShiftedTo( 12 ),
  2664. expected.newRange.end.getShiftedTo( 14 )
  2665. );
  2666. expect( transOp.length ).to.equal( 1 );
  2667. expectOperation( transOp[ 0 ], expected );
  2668. } );
  2669. } );
  2670. describe( 'by AttributeOperation', () => {
  2671. it( 'no operation update', () => {
  2672. const transformBy = new AttributeOperation(
  2673. new Range(
  2674. new Position( root, [ 2 ] ),
  2675. new Position( root, [ 11 ] )
  2676. ),
  2677. 'foo',
  2678. 'bar',
  2679. 'xyz',
  2680. baseVersion
  2681. );
  2682. const transOp = transform( op, transformBy );
  2683. expect( transOp.length ).to.equal( 1 );
  2684. expectOperation( transOp[ 0 ], expected );
  2685. } );
  2686. } );
  2687. describe( 'by MoveOperation', () => {
  2688. it( 'moved range is before oldRange: update oldRange', () => {
  2689. // Just CC things.
  2690. op.newRange = null;
  2691. const transformBy = new MoveOperation( Position.createAt( root, 0 ), 1, Position.createAt( root, 20 ), baseVersion );
  2692. const transOp = transform( op, transformBy );
  2693. expected.newRange = null;
  2694. expected.oldRange = new Range(
  2695. expected.oldRange.start.getShiftedTo( 0 ),
  2696. expected.oldRange.end.getShiftedTo( 3 )
  2697. );
  2698. expect( transOp.length ).to.equal( 1 );
  2699. expectOperation( transOp[ 0 ], expected );
  2700. } );
  2701. it( 'moved range contains oldRange and is before newRange: update oldRange and newRange', () => {
  2702. const transformBy = new MoveOperation( Position.createAt( root, 2 ), 2, Position.createAt( root, 20 ), baseVersion );
  2703. const transOp = transform( op, transformBy );
  2704. expected.oldRange = new Range(
  2705. expected.oldRange.start.getShiftedTo( 1 ),
  2706. expected.oldRange.end.getShiftedTo( 2 )
  2707. );
  2708. expected.newRange = new Range(
  2709. expected.newRange.start.getShiftedTo( 8 ),
  2710. expected.newRange.end.getShiftedTo( 10 )
  2711. );
  2712. expect( transOp.length ).to.equal( 1 );
  2713. expectOperation( transOp[ 0 ], expected );
  2714. } );
  2715. it( 'target position is inside newRange: update newRange', () => {
  2716. // Just CC things.
  2717. op.oldRange = null;
  2718. const transformBy = new MoveOperation( Position.createAt( root, 20 ), 2, Position.createAt( root, 11 ), baseVersion );
  2719. const transOp = transform( op, transformBy );
  2720. expected.oldRange = null;
  2721. expected.newRange = new Range(
  2722. expected.newRange.start.getShiftedTo( 10 ),
  2723. expected.newRange.end.getShiftedTo( 14 )
  2724. );
  2725. expect( transOp.length ).to.equal( 1 );
  2726. expectOperation( transOp[ 0 ], expected );
  2727. } );
  2728. it( 'target position is inside oldRange and before newRange: update oldRange and newRange', () => {
  2729. const transformBy = new MoveOperation( Position.createAt( root, 20 ), 4, Position.createAt( root, 2 ), baseVersion );
  2730. const transOp = transform( op, transformBy );
  2731. expected.oldRange = new Range(
  2732. expected.oldRange.start.getShiftedTo( 1 ),
  2733. expected.oldRange.end.getShiftedTo( 8 )
  2734. );
  2735. expected.newRange = new Range(
  2736. expected.newRange.start.getShiftedTo( 14 ),
  2737. expected.newRange.end.getShiftedTo( 16 )
  2738. );
  2739. expect( transOp.length ).to.equal( 1 );
  2740. expectOperation( transOp[ 0 ], expected );
  2741. } );
  2742. } );
  2743. describe( 'by RootAttributeOperation', () => {
  2744. it( 'no operation update', () => {
  2745. const transformBy = new RootAttributeOperation(
  2746. root,
  2747. 'foo',
  2748. null,
  2749. 'bar',
  2750. baseVersion
  2751. );
  2752. const transOp = transform( op, transformBy );
  2753. expect( transOp.length ).to.equal( 1 );
  2754. expectOperation( transOp[ 0 ], expected );
  2755. } );
  2756. } );
  2757. describe( 'by RenameOperation', () => {
  2758. it( 'no operation update', () => {
  2759. const transformBy = new RenameOperation( new Position( root, [ 1 ] ), 'oldName', 'newName', baseVersion );
  2760. const transOp = transform( op, transformBy );
  2761. expect( transOp.length ).to.equal( 1 );
  2762. expectOperation( transOp[ 0 ], expected );
  2763. } );
  2764. } );
  2765. describe( 'by MarkerOperation', () => {
  2766. it( 'different marker name: no operation update', () => {
  2767. const transformBy = new MarkerOperation( 'otherName', oldRange, newRange, doc.markers, baseVersion );
  2768. const transOp = transform( op, transformBy );
  2769. expect( transOp.length ).to.equal( 1 );
  2770. expectOperation( transOp[ 0 ], expected );
  2771. } );
  2772. it( 'same marker name and is important: convert to NoOperation', () => {
  2773. const anotherRange = Range.createFromParentsAndOffsets( root, 2, root, 2 );
  2774. const transformBy = new MarkerOperation( 'name', oldRange, anotherRange, doc.markers, baseVersion );
  2775. const transOp = transform( op, transformBy );
  2776. expect( transOp.length ).to.equal( 1 );
  2777. expectOperation( transOp[ 0 ], {
  2778. type: NoOperation,
  2779. baseVersion: baseVersion + 1
  2780. } );
  2781. } );
  2782. it( 'same marker name and is less important: update oldRange parameter', () => {
  2783. const anotherRange = Range.createFromParentsAndOffsets( root, 2, root, 2 );
  2784. const transformBy = new MarkerOperation( 'name', oldRange, anotherRange, doc.markers, baseVersion );
  2785. const transOp = transform( op, transformBy, { isStrong: true } );
  2786. expected.oldRange = anotherRange;
  2787. expect( transOp.length ).to.equal( 1 );
  2788. expectOperation( transOp[ 0 ], expected );
  2789. } );
  2790. } );
  2791. } );
  2792. } );