enableenginedebug.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import enableEngineDebug from '../../src/dev-utils/enableenginedebug';
  6. import StandardEditor from '@ckeditor/ckeditor5-core/src/editor/standardeditor';
  7. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  8. import ModelPosition from '../../src/model/position';
  9. import ModelRange from '../../src/model/range';
  10. import ModelText from '../../src/model/text';
  11. import ModelTextProxy from '../../src/model/textproxy';
  12. import ModelElement from '../../src/model/element';
  13. import AttributeOperation from '../../src/model/operation/attributeoperation';
  14. import DetachOperation from '../../src/model/operation/detachoperation';
  15. import InsertOperation from '../../src/model/operation/insertoperation';
  16. import MarkerOperation from '../../src/model/operation/markeroperation';
  17. import MoveOperation from '../../src/model/operation/moveoperation';
  18. import NoOperation from '../../src/model/operation/nooperation';
  19. import RenameOperation from '../../src/model/operation/renameoperation';
  20. import RootAttributeOperation from '../../src/model/operation/rootattributeoperation';
  21. import RemoveOperation from '../../src/model/operation/removeoperation';
  22. import DeltaFactory from '../../src/model/delta/deltafactory';
  23. import Delta from '../../src/model/delta/delta';
  24. import AttributeDelta from '../../src/model/delta/attributedelta';
  25. import InsertDelta from '../../src/model/delta/insertdelta';
  26. import MarkerDelta from '../../src/model/delta/markerdelta';
  27. import MergeDelta from '../../src/model/delta/mergedelta';
  28. import MoveDelta from '../../src/model/delta/movedelta';
  29. import RenameDelta from '../../src/model/delta/renamedelta';
  30. import RootAttributeDelta from '../../src/model/delta/rootattributedelta';
  31. import SplitDelta from '../../src/model/delta/splitdelta';
  32. import UnwrapDelta from '../../src/model/delta/unwrapdelta';
  33. import WrapDelta from '../../src/model/delta/wrapdelta';
  34. import deltaTransform from '../../src/model/delta/transform';
  35. import ModelDocument from '../../src/model/document';
  36. import ModelDocumentFragment from '../../src/model/documentfragment';
  37. import ViewDocument from '../../src/view/document';
  38. import ViewAttributeElement from '../../src/view/attributeelement';
  39. import ViewContainerElement from '../../src/view/containerelement';
  40. import ViewText from '../../src/view/text';
  41. import ViewTextProxy from '../../src/view/textproxy';
  42. import ViewDocumentFragment from '../../src/view/documentfragment';
  43. /* global document */
  44. describe( 'enableEngineDebug', () => {
  45. it( 'should return plugin class', () => {
  46. const result = enableEngineDebug();
  47. expect( result.prototype ).to.be.instanceof( Plugin );
  48. } );
  49. it( 'should not throw when called multiple times', () => {
  50. enableEngineDebug();
  51. enableEngineDebug();
  52. const result = enableEngineDebug();
  53. expect( result.prototype ).to.be.instanceof( Plugin );
  54. } );
  55. } );
  56. describe( 'debug tools', () => {
  57. let DebugPlugin, log, error;
  58. class TestEditor extends StandardEditor {
  59. constructor( ...args ) {
  60. super( ...args );
  61. this.document.createRoot( 'main' );
  62. this.editing.createRoot( this.element, 'main' );
  63. }
  64. }
  65. before( () => {
  66. log = sinon.spy();
  67. error = sinon.spy();
  68. DebugPlugin = enableEngineDebug( { log, error } );
  69. } );
  70. afterEach( () => {
  71. log.reset();
  72. } );
  73. describe( 'should provide logging tools', () => {
  74. let modelDoc, modelRoot, modelElement, modelDocFrag;
  75. beforeEach( () => {
  76. modelDoc = new ModelDocument();
  77. modelRoot = modelDoc.createRoot();
  78. modelElement = new ModelElement( 'paragraph', null, new ModelText( 'foo' ) );
  79. modelDocFrag = new ModelDocumentFragment( [ new ModelText( 'bar' ) ] );
  80. } );
  81. it( 'for ModelText', () => {
  82. const foo = new ModelText( 'foo', { foo: 'bar' } );
  83. expect( foo.toString() ).to.equal( '#foo' );
  84. foo.log();
  85. expect( log.calledWithExactly( 'ModelText: #foo' ) ).to.be.true;
  86. foo.logExtended();
  87. expect( log.calledWithExactly( 'ModelText: #foo, attrs: {"foo":"bar"}' ) ).to.be.true;
  88. } );
  89. it( 'for ModelTextProxy', () => {
  90. const foo = new ModelText( 'foo', { foo: 'bar' } );
  91. const proxy = new ModelTextProxy( foo, 1, 1 );
  92. expect( proxy.toString() ).to.equal( '#o' );
  93. proxy.log();
  94. expect( log.calledWithExactly( 'ModelTextProxy: #o' ) ).to.be.true;
  95. proxy.logExtended();
  96. expect( log.calledWithExactly( 'ModelTextProxy: #o, attrs: {"foo":"bar"}' ) ).to.be.true;
  97. } );
  98. it( 'for ModelElement', () => {
  99. const paragraph = new ModelElement( 'paragraph', { foo: 'bar' }, new ModelText( 'foo' ) );
  100. expect( paragraph.toString() ).to.equal( '<paragraph>' );
  101. paragraph.log();
  102. expectLog( 'ModelElement: <paragraph>' );
  103. paragraph.logExtended();
  104. expectLog( 'ModelElement: <paragraph>, 1 children, attrs: {"foo":"bar"}' );
  105. sinon.spy( paragraph, 'logExtended' );
  106. paragraph.logAll();
  107. expect( paragraph.logExtended.called ).to.be.true;
  108. expectLog( 'ModelText: #foo' );
  109. } );
  110. it( 'for ModelRootElement', () => {
  111. modelRoot.log();
  112. expectLog( 'ModelRootElement: main' );
  113. } );
  114. it( 'for ModelDocumentFragment', () => {
  115. modelDocFrag.log();
  116. expectLog( 'ModelDocumentFragment: documentFragment' );
  117. } );
  118. it( 'for ModelPosition', () => {
  119. const posInRoot = new ModelPosition( modelRoot, [ 0, 1, 0 ] );
  120. const posInElement = new ModelPosition( modelElement, [ 0 ] );
  121. const posInDocFrag = new ModelPosition( modelDocFrag, [ 2, 3 ] );
  122. expect( posInRoot.toString() ).to.equal( 'main [ 0, 1, 0 ]' );
  123. expect( posInElement.toString() ).to.equal( '<paragraph> [ 0 ]' );
  124. expect( posInDocFrag.toString() ).to.equal( 'documentFragment [ 2, 3 ]' );
  125. posInRoot.log();
  126. expectLog( 'ModelPosition: main [ 0, 1, 0 ]' );
  127. } );
  128. it( 'for ModelRange', () => {
  129. const rangeInRoot = ModelRange.createIn( modelRoot );
  130. const rangeInElement = ModelRange.createIn( modelElement );
  131. const rangeInDocFrag = ModelRange.createIn( modelDocFrag );
  132. expect( rangeInRoot.toString() ).to.equal( 'main [ 0 ] - [ 0 ]' );
  133. expect( rangeInElement.toString() ).to.equal( '<paragraph> [ 0 ] - [ 3 ]' );
  134. expect( rangeInDocFrag.toString() ).to.equal( 'documentFragment [ 0 ] - [ 3 ]' );
  135. rangeInRoot.log();
  136. expectLog( 'ModelRange: main [ 0 ] - [ 0 ]' );
  137. } );
  138. it( 'for ViewText', () => {
  139. const foo = new ViewText( 'foo' );
  140. expect( foo.toString() ).to.equal( '#foo' );
  141. foo.log();
  142. expect( log.calledWithExactly( 'ViewText: #foo' ) ).to.be.true;
  143. foo.logExtended();
  144. expect( log.calledWithExactly( 'ViewText: #foo' ) ).to.be.true;
  145. } );
  146. it( 'for ViewTextProxy', () => {
  147. const foo = new ViewText( 'foo', { foo: 'bar' } );
  148. const proxy = new ViewTextProxy( foo, 1, 1 );
  149. expect( proxy.toString() ).to.equal( '#o' );
  150. proxy.log();
  151. expect( log.calledWithExactly( 'ViewTextProxy: #o' ) ).to.be.true;
  152. proxy.logExtended();
  153. expect( log.calledWithExactly( 'ViewTextProxy: #o' ) ).to.be.true;
  154. } );
  155. describe( 'for operations', () => {
  156. beforeEach( () => {
  157. modelRoot.appendChildren( [ new ModelText( 'foobar' ) ] );
  158. } );
  159. it( 'AttributeOperation', () => {
  160. const op = new AttributeOperation( ModelRange.createIn( modelRoot ), 'key', null, { foo: 'bar' }, 0 );
  161. expect( op.toString() ).to.equal( 'AttributeOperation( 0 ): "key": null -> {"foo":"bar"}, main [ 0 ] - [ 6 ]' );
  162. op.log();
  163. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  164. } );
  165. it( 'DetachOperation (text node)', () => {
  166. const op = new DetachOperation( ModelPosition.createAt( modelRoot, 0 ), 3, 0 );
  167. expect( op.toString() ).to.equal( 'DetachOperation( 0 ): #foo -> main [ 0 ] - [ 3 ]' );
  168. op.log();
  169. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  170. } );
  171. it( 'DetachOperation (element)', () => {
  172. const element = new ModelElement( 'element' );
  173. modelRoot.insertChildren( 0, element );
  174. const op = new DetachOperation( ModelPosition.createBefore( element ), 1, 0 );
  175. expect( op.toString() ).to.equal( 'DetachOperation( 0 ): <element> -> main [ 0 ] - [ 1 ]' );
  176. op.log();
  177. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  178. } );
  179. it( 'DetachOperation (multiple nodes)', () => {
  180. const element = new ModelElement( 'element' );
  181. modelRoot.insertChildren( 0, element );
  182. const op = new DetachOperation( ModelPosition.createBefore( element ), 2, 0 );
  183. expect( op.toString() ).to.equal( 'DetachOperation( 0 ): [ 2 ] -> main [ 0 ] - [ 2 ]' );
  184. op.log();
  185. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  186. } );
  187. it( 'InsertOperation (text node)', () => {
  188. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelText( 'abc' ) ], 0 );
  189. expect( op.toString() ).to.equal( 'InsertOperation( 0 ): #abc -> main [ 3 ]' );
  190. op.log();
  191. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  192. } );
  193. it( 'InsertOperation (element)', () => {
  194. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelElement( 'paragraph' ) ], 0 );
  195. expect( op.toString() ).to.equal( 'InsertOperation( 0 ): <paragraph> -> main [ 3 ]' );
  196. op.log();
  197. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  198. } );
  199. it( 'InsertOperation (multiple nodes)', () => {
  200. const nodes = [ new ModelText( 'x' ), new ModelElement( 'y' ), new ModelText( 'z' ) ];
  201. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), nodes, 0 );
  202. expect( op.toString() ).to.equal( 'InsertOperation( 0 ): [ 3 ] -> main [ 3 ]' );
  203. op.log();
  204. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  205. } );
  206. it( 'MarkerOperation', () => {
  207. const op = new MarkerOperation( 'marker', null, ModelRange.createIn( modelRoot ), modelDoc.markers, 0 );
  208. expect( op.toString() ).to.equal( 'MarkerOperation( 0 ): "marker": null -> main [ 0 ] - [ 6 ]' );
  209. op.log();
  210. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  211. } );
  212. it( 'MoveOperation', () => {
  213. const op = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, ModelPosition.createAt( modelRoot, 6 ), 0 );
  214. expect( op.toString() ).to.equal( 'MoveOperation( 0 ): main [ 1 ] - [ 3 ] -> main [ 6 ]' );
  215. op.log();
  216. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  217. } );
  218. it( 'MoveOperation sticky', () => {
  219. const op = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, ModelPosition.createAt( modelRoot, 6 ), 0 );
  220. op.isSticky = true;
  221. expect( op.toString() ).to.equal( 'MoveOperation( 0 ): main [ 1 ] - [ 3 ] -> main [ 6 ] (sticky)' );
  222. op.log();
  223. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  224. } );
  225. it( 'NoOperation', () => {
  226. const op = new NoOperation( 0 );
  227. expect( op.toString() ).to.equal( 'NoOperation( 0 )' );
  228. op.log();
  229. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  230. } );
  231. it( 'RenameOperation', () => {
  232. const op = new RenameOperation( ModelPosition.createAt( modelRoot, 1 ), 'old', 'new', 0 );
  233. expect( op.toString() ).to.equal( 'RenameOperation( 0 ): main [ 1 ]: "old" -> "new"' );
  234. op.log();
  235. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  236. } );
  237. it( 'RootAttributeOperation', () => {
  238. const op = new RootAttributeOperation( modelRoot, 'key', 'old', null, 0 );
  239. expect( op.toString() ).to.equal( 'RootAttributeOperation( 0 ): "key": "old" -> null, main' );
  240. op.log();
  241. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  242. } );
  243. } );
  244. describe( 'for deltas', () => {
  245. it( 'Delta', () => {
  246. const delta = new Delta();
  247. const op = { log: sinon.spy() };
  248. delta.addOperation( op );
  249. sinon.spy( delta, 'log' );
  250. delta.logAll();
  251. expect( op.log.called ).to.be.true;
  252. expect( delta.log.called ).to.be.true;
  253. } );
  254. it( 'AttributeDelta', () => {
  255. modelRoot.appendChildren( new ModelText( 'foobar' ) );
  256. const delta = new AttributeDelta();
  257. const op = new AttributeOperation( ModelRange.createIn( modelRoot ), 'key', null, { foo: 'bar' }, 0 );
  258. delta.addOperation( op );
  259. expect( delta.toString() ).to.equal( 'AttributeDelta( 0 ): "key": -> {"foo":"bar"}, main [ 0 ] - [ 6 ], 1 ops' );
  260. delta.log();
  261. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  262. } );
  263. it( 'InsertDelta (text node)', () => {
  264. const delta = new InsertDelta();
  265. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelText( 'abc' ) ], 0 );
  266. delta.addOperation( op );
  267. expect( delta.toString() ).to.equal( 'InsertDelta( 0 ): #abc -> main [ 3 ]' );
  268. delta.log();
  269. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  270. } );
  271. it( 'InsertDelta (element)', () => {
  272. const delta = new InsertDelta();
  273. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelElement( 'paragraph' ) ], 0 );
  274. delta.addOperation( op );
  275. expect( delta.toString() ).to.equal( 'InsertDelta( 0 ): <paragraph> -> main [ 3 ]' );
  276. delta.log();
  277. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  278. } );
  279. it( 'InsertDelta (multiple nodes)', () => {
  280. const delta = new InsertDelta();
  281. const nodes = [ new ModelText( 'x' ), new ModelElement( 'y' ), new ModelText( 'z' ) ];
  282. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), nodes, 0 );
  283. delta.addOperation( op );
  284. expect( delta.toString() ).to.equal( 'InsertDelta( 0 ): [ 3 ] -> main [ 3 ]' );
  285. delta.log();
  286. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  287. } );
  288. it( 'MarkerDelta', () => {
  289. modelRoot.appendChildren( new ModelText( 'foobar' ) );
  290. const delta = new MarkerDelta();
  291. const op = new MarkerOperation( 'marker', null, ModelRange.createIn( modelRoot ), modelDoc.markers, 0 );
  292. delta.addOperation( op );
  293. expect( delta.toString() ).to.equal( 'MarkerDelta( 0 ): "marker": null -> main [ 0 ] - [ 6 ]' );
  294. delta.log();
  295. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  296. } );
  297. it( 'MergeDelta', () => {
  298. const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
  299. const firstEle = new ModelElement( 'paragraph' );
  300. const removedEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
  301. otherRoot.appendChildren( [ firstEle, removedEle ] );
  302. const graveyard = modelDoc.graveyard;
  303. const delta = new MergeDelta();
  304. const move = new MoveOperation( ModelPosition.createAt( removedEle, 0 ), 3, ModelPosition.createAt( firstEle, 0 ), 0 );
  305. const remove = new RemoveOperation(
  306. ModelPosition.createBefore( removedEle ),
  307. 1,
  308. ModelPosition.createAt( graveyard, 0 ),
  309. 1
  310. );
  311. delta.addOperation( move );
  312. delta.addOperation( remove );
  313. expect( delta.toString() ).to.equal( 'MergeDelta( 0 ): otherRoot [ 1 ]' );
  314. delta.log();
  315. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  316. } );
  317. it( 'MergeDelta - NoOperation as second operation', () => {
  318. const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
  319. const firstEle = new ModelElement( 'paragraph' );
  320. const removedEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
  321. otherRoot.appendChildren( [ firstEle, removedEle ] );
  322. const delta = new MergeDelta();
  323. const move = new MoveOperation( ModelPosition.createAt( removedEle, 0 ), 3, ModelPosition.createAt( firstEle, 0 ), 0 );
  324. delta.addOperation( move );
  325. delta.addOperation( new NoOperation( 1 ) );
  326. expect( delta.toString() ).to.equal( 'MergeDelta( 0 ): (move from otherRoot [ 1, 0 ])' );
  327. delta.log();
  328. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  329. } );
  330. it( 'MoveDelta', () => {
  331. const delta = new MoveDelta();
  332. const move1 = new MoveOperation( ModelPosition.createAt( modelRoot, 0 ), 1, ModelPosition.createAt( modelRoot, 3 ), 0 );
  333. const move2 = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 1, ModelPosition.createAt( modelRoot, 6 ), 0 );
  334. delta.addOperation( move1 );
  335. delta.addOperation( move2 );
  336. expect( delta.toString() ).to.equal( 'MoveDelta( 0 ): main [ 0 ] - [ 1 ] -> main [ 3 ]; main [ 1 ] - [ 2 ] -> main [ 6 ]' );
  337. delta.log();
  338. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  339. } );
  340. it( 'RenameDelta', () => {
  341. const delta = new RenameDelta();
  342. const op = new RenameOperation( ModelPosition.createAt( modelRoot, 1 ), 'old', 'new', 0 );
  343. delta.addOperation( op );
  344. expect( delta.toString() ).to.equal( 'RenameDelta( 0 ): main [ 1 ]: "old" -> "new"' );
  345. delta.log();
  346. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  347. } );
  348. it( 'RootAttributeDelta', () => {
  349. const delta = new RootAttributeDelta();
  350. const op = new RootAttributeOperation( modelRoot, 'key', 'old', null, 0 );
  351. delta.addOperation( op );
  352. expect( delta.toString() ).to.equal( 'RootAttributeDelta( 0 ): "key": "old" -> null, main' );
  353. delta.log();
  354. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  355. } );
  356. it( 'SplitDelta', () => {
  357. const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
  358. const splitEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
  359. otherRoot.appendChildren( [ splitEle ] );
  360. const delta = new SplitDelta();
  361. const insert = new InsertOperation( ModelPosition.createAt( otherRoot, 1 ), [ new ModelElement( 'paragraph' ) ], 0 );
  362. const move = new MoveOperation( ModelPosition.createAt( splitEle, 1 ), 2, new ModelPosition( otherRoot, [ 1, 0 ] ), 1 );
  363. delta.addOperation( insert );
  364. delta.addOperation( move );
  365. expect( delta.toString() ).to.equal( 'SplitDelta( 0 ): otherRoot [ 0, 1 ]' );
  366. delta.log();
  367. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  368. } );
  369. it( 'SplitDelta - NoOperation as second operation', () => {
  370. const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
  371. const splitEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
  372. otherRoot.appendChildren( [ splitEle ] );
  373. const delta = new SplitDelta();
  374. const insert = new InsertOperation( ModelPosition.createAt( otherRoot, 1 ), [ new ModelElement( 'paragraph' ) ], 0 );
  375. const move = new NoOperation( 1 );
  376. delta.addOperation( insert );
  377. delta.addOperation( move );
  378. expect( delta.toString() ).to.equal( 'SplitDelta( 0 ): (clone to otherRoot [ 1 ])' );
  379. delta.log();
  380. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  381. } );
  382. it( 'SplitDelta - NoOperation as second operation, MoveOperation as first operation', () => {
  383. const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
  384. const delta = new SplitDelta();
  385. const insert = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 1, ModelPosition.createAt( otherRoot, 1 ), 0 );
  386. const move = new NoOperation( 1 );
  387. delta.addOperation( insert );
  388. delta.addOperation( move );
  389. expect( delta.toString() ).to.equal( 'SplitDelta( 0 ): (clone to otherRoot [ 1 ])' );
  390. delta.log();
  391. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  392. } );
  393. it( 'UnwrapDelta', () => {
  394. const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
  395. const unwrapEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
  396. otherRoot.appendChildren( [ unwrapEle ] );
  397. const graveyard = modelDoc.graveyard;
  398. const delta = new UnwrapDelta();
  399. const move = new MoveOperation( ModelPosition.createAt( unwrapEle, 0 ), 3, ModelPosition.createAt( otherRoot, 0 ), 0 );
  400. const remove = new RemoveOperation( ModelPosition.createAt( otherRoot, 3 ), 1, ModelPosition.createAt( graveyard, 0 ), 1 );
  401. delta.addOperation( move );
  402. delta.addOperation( remove );
  403. expect( delta.toString() ).to.equal( 'UnwrapDelta( 0 ): otherRoot [ 0 ]' );
  404. delta.log();
  405. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  406. } );
  407. it( 'WrapDelta', () => {
  408. const delta = new WrapDelta();
  409. const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 6 ), new ModelElement( 'paragraph' ), 0 );
  410. const move = new MoveOperation( ModelPosition.createAt( modelRoot, 0 ), 6, new ModelPosition( modelRoot, [ 1, 0 ] ), 1 );
  411. delta.addOperation( insert );
  412. delta.addOperation( move );
  413. expect( delta.toString() ).to.equal( 'WrapDelta( 0 ): main [ 0 ] - [ 6 ] -> <paragraph>' );
  414. delta.log();
  415. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  416. } );
  417. } );
  418. it( 'for applied operations', () => {
  419. const delta = new InsertDelta();
  420. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), [ new ModelText( 'foo' ) ], 0 );
  421. delta.addOperation( op );
  422. modelDoc.applyOperation( op );
  423. expect( log.calledWithExactly( 'Applying InsertOperation( 0 ): #foo -> main [ 0 ]' ) ).to.be.true;
  424. } );
  425. } );
  426. describe( 'should provide tree printing tools', () => {
  427. it( 'for model', () => {
  428. const modelDoc = new ModelDocument();
  429. const modelRoot = modelDoc.createRoot();
  430. modelRoot.appendChildren( [
  431. new ModelElement( 'paragraph', { foo: 'bar' }, [
  432. new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' )
  433. ] ),
  434. new ModelElement( 'listItem', { type: 'numbered', indent: 0 }, new ModelText( 'One.' ) ),
  435. ] );
  436. const modelRootTree = modelRoot.printTree();
  437. expect( modelRootTree ).to.equal(
  438. '<main>' +
  439. '\n\t<paragraph foo="bar">' +
  440. '\n\t\tThis is ' +
  441. '\n\t\t<$text bold=true>bold</$text>' +
  442. '\n\t\t.' +
  443. '\n\t</paragraph>' +
  444. '\n\t<listItem type="numbered" indent=0>' +
  445. '\n\t\tOne.' +
  446. '\n\t</listItem>' +
  447. '\n</main>'
  448. );
  449. modelRoot.logTree();
  450. expect( log.calledWithExactly( modelRootTree ) ).to.be.true;
  451. const modelParagraph = modelRoot.getChild( 0 );
  452. const modelParagraphTree = modelParagraph.printTree();
  453. expect( modelParagraphTree ).to.equal(
  454. '<paragraph foo="bar">' +
  455. '\n\tThis is ' +
  456. '\n\t<$text bold=true>bold</$text>' +
  457. '\n\t.' +
  458. '\n</paragraph>'
  459. );
  460. log.reset();
  461. modelParagraph.logTree();
  462. expect( log.calledWithExactly( modelParagraphTree ) ).to.be.true;
  463. const modelDocFrag = new ModelDocumentFragment( [
  464. new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' ),
  465. new ModelElement( 'paragraph', { foo: 'bar' }, [
  466. new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' )
  467. ] )
  468. ] );
  469. const modelDocFragTree = modelDocFrag.printTree();
  470. expect( modelDocFragTree ).to.equal(
  471. 'ModelDocumentFragment: [' +
  472. '\n\tThis is ' +
  473. '\n\t<$text bold=true>bold</$text>' +
  474. '\n\t.' +
  475. '\n\t<paragraph foo="bar">' +
  476. '\n\t\tThis is ' +
  477. '\n\t\t<$text bold=true>bold</$text>' +
  478. '\n\t\t.' +
  479. '\n\t</paragraph>' +
  480. '\n]'
  481. );
  482. log.reset();
  483. modelDocFrag.logTree();
  484. expect( log.calledWithExactly( modelDocFragTree ) ).to.be.true;
  485. } );
  486. it( 'for view', () => {
  487. const viewDoc = new ViewDocument();
  488. const viewRoot = viewDoc.createRoot( 'div' );
  489. viewRoot.appendChildren( [
  490. new ViewContainerElement( 'p', { foo: 'bar' }, [
  491. new ViewText( 'This is ' ), new ViewAttributeElement( 'b', null, new ViewText( 'bold' ) ), new ViewText( '.' )
  492. ] ),
  493. new ViewContainerElement( 'ol', null, [
  494. new ViewContainerElement( 'li', null, new ViewText( 'One.' ) )
  495. ] )
  496. ] );
  497. const viewRootTree = viewRoot.printTree();
  498. expect( viewRootTree ).to.equal(
  499. '<div>' +
  500. '\n\t<p foo="bar">' +
  501. '\n\t\tThis is ' +
  502. '\n\t\t<b>' +
  503. '\n\t\t\tbold' +
  504. '\n\t\t</b>' +
  505. '\n\t\t.' +
  506. '\n\t</p>' +
  507. '\n\t<ol>' +
  508. '\n\t\t<li>' +
  509. '\n\t\t\tOne.' +
  510. '\n\t\t</li>' +
  511. '\n\t</ol>' +
  512. '\n</div>'
  513. );
  514. viewRoot.logTree();
  515. expect( log.calledWithExactly( viewRootTree ) ).to.be.true;
  516. const viewParagraph = viewRoot.getChild( 0 );
  517. const viewParagraphTree = viewParagraph.printTree();
  518. expect( viewParagraphTree ).to.equal(
  519. '<p foo="bar">' +
  520. '\n\tThis is ' +
  521. '\n\t<b>' +
  522. '\n\t\tbold' +
  523. '\n\t</b>' +
  524. '\n\t.' +
  525. '\n</p>'
  526. );
  527. log.reset();
  528. viewParagraph.logTree();
  529. expect( log.calledWithExactly( viewParagraphTree ) ).to.be.true;
  530. const viewDocFrag = new ViewDocumentFragment( [
  531. new ViewText( 'Text.' ),
  532. new ViewContainerElement( 'p', { foo: 'bar' }, [
  533. new ViewText( 'This is ' ), new ViewAttributeElement( 'b', null, new ViewText( 'bold' ) ), new ViewText( '.' )
  534. ] )
  535. ] );
  536. const viewDocFragTree = viewDocFrag.printTree();
  537. expect( viewDocFragTree ).to.equal(
  538. 'ViewDocumentFragment: [' +
  539. '\n\tText.' +
  540. '\n\t<p foo="bar">' +
  541. '\n\t\tThis is ' +
  542. '\n\t\t<b>' +
  543. '\n\t\t\tbold' +
  544. '\n\t\t</b>' +
  545. '\n\t\t.' +
  546. '\n\t</p>' +
  547. '\n]'
  548. );
  549. log.reset();
  550. viewDocFrag.logTree();
  551. expect( log.calledWithExactly( viewDocFragTree ) ).to.be.true;
  552. } );
  553. } );
  554. describe( 'should store model and view trees state', () => {
  555. let editor;
  556. beforeEach( () => {
  557. const div = document.createElement( 'div' );
  558. return TestEditor.create( div, {
  559. plugins: [ DebugPlugin ]
  560. } ).then( _editor => {
  561. editor = _editor;
  562. } );
  563. } );
  564. it( 'should store model and view state after each applied operation', () => {
  565. const model = editor.document;
  566. const modelRoot = model.getRoot();
  567. const view = editor.editing.view;
  568. const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), new ModelText( 'foobar' ), 0 );
  569. model.applyOperation( wrapInDelta( insert ) );
  570. const graveyard = model.graveyard;
  571. const remove = new RemoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, ModelPosition.createAt( graveyard, 0 ), 1 );
  572. model.applyOperation( wrapInDelta( remove ) );
  573. log.reset();
  574. model.log( 0 );
  575. expectLog(
  576. '<$graveyard></$graveyard>' +
  577. '\n<main></main>'
  578. );
  579. model.log( 1 );
  580. expectLog(
  581. '<$graveyard></$graveyard>' +
  582. '\n<main>' +
  583. '\n\tfoobar' +
  584. '\n</main>'
  585. );
  586. model.log( 2 );
  587. expectLog(
  588. '<$graveyard>' +
  589. '\n\too' +
  590. '\n</$graveyard>' +
  591. '\n<main>' +
  592. '\n\tfbar' +
  593. '\n</main>'
  594. );
  595. model.log();
  596. expectLog(
  597. '<$graveyard>' +
  598. '\n\too' +
  599. '\n</$graveyard>' +
  600. '\n<main>' +
  601. '\n\tfbar' +
  602. '\n</main>'
  603. );
  604. view.log( 0 );
  605. expectLog(
  606. '<div></div>'
  607. );
  608. view.log( 1 );
  609. expectLog(
  610. '<div>' +
  611. '\n\tfoobar' +
  612. '\n</div>'
  613. );
  614. view.log( 2 );
  615. expectLog(
  616. '<div>' +
  617. '\n\tfbar' +
  618. '\n</div>'
  619. );
  620. sinon.spy( model, 'log' );
  621. sinon.spy( view, 'log' );
  622. editor.logModel( 1 );
  623. expect( model.log.calledWithExactly( 1 ) ).to.be.true;
  624. editor.logView( 2 );
  625. expect( view.log.calledWithExactly( 2 ) ).to.be.true;
  626. model.log.reset();
  627. view.log.reset();
  628. editor.logModel();
  629. expect( model.log.calledWithExactly( 2 ) ).to.be.true;
  630. model.log.reset();
  631. view.log.reset();
  632. editor.logDocuments();
  633. expect( model.log.calledWithExactly( 2 ) ).to.be.true;
  634. expect( view.log.calledWithExactly( 2 ) ).to.be.true;
  635. model.log.reset();
  636. view.log.reset();
  637. editor.logDocuments( 1 );
  638. expect( model.log.calledWithExactly( 1 ) ).to.be.true;
  639. expect( view.log.calledWithExactly( 1 ) ).to.be.true;
  640. } );
  641. it( 'should remove old states', () => {
  642. const model = editor.document;
  643. const modelRoot = model.getRoot();
  644. for ( let i = 0; i < 25; i++ ) {
  645. const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), new ModelText( 'foobar' ), model.version );
  646. model.applyOperation( wrapInDelta( insert ) );
  647. }
  648. model.log( 0 );
  649. expectLog( 'Tree log unavailable for given version: 0' );
  650. } );
  651. } );
  652. describe( 'should provide methods for delta replayer', () => {
  653. it( 'getAppliedDeltas()', () => {
  654. const modelDoc = new ModelDocument();
  655. expect( modelDoc.getAppliedDeltas() ).to.equal( '' );
  656. const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
  657. const firstEle = new ModelElement( 'paragraph' );
  658. const removedEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
  659. otherRoot.appendChildren( [ firstEle, removedEle ] );
  660. const delta = new MergeDelta();
  661. const graveyard = modelDoc.graveyard;
  662. const move = new MoveOperation( ModelPosition.createAt( removedEle, 0 ), 3, ModelPosition.createAt( firstEle, 0 ), 0 );
  663. const remove = new RemoveOperation( ModelPosition.createBefore( removedEle ), 1, ModelPosition.createAt( graveyard, 0 ), 1 );
  664. delta.addOperation( move );
  665. delta.addOperation( remove );
  666. modelDoc.applyOperation( move );
  667. modelDoc.applyOperation( remove );
  668. const stringifiedDeltas = modelDoc.getAppliedDeltas();
  669. expect( stringifiedDeltas ).to.equal( JSON.stringify( delta.toJSON() ) );
  670. } );
  671. it( 'createReplayer()', () => {
  672. const modelDoc = new ModelDocument();
  673. const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
  674. const firstEle = new ModelElement( 'paragraph' );
  675. const removedEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
  676. otherRoot.appendChildren( [ firstEle, removedEle ] );
  677. const delta = new MergeDelta();
  678. const graveyard = modelDoc.graveyard;
  679. const move = new MoveOperation( ModelPosition.createAt( removedEle, 0 ), 3, ModelPosition.createAt( firstEle, 0 ), 0 );
  680. const remove = new RemoveOperation( ModelPosition.createBefore( removedEle ), 1, ModelPosition.createAt( graveyard, 0 ), 1 );
  681. delta.addOperation( move );
  682. delta.addOperation( remove );
  683. const stringifiedDeltas = JSON.stringify( delta.toJSON() );
  684. const deltaReplayer = modelDoc.createReplayer( stringifiedDeltas );
  685. expect( deltaReplayer.getDeltasToReplay() ).to.deep.equal( [ JSON.parse( stringifiedDeltas ) ] );
  686. } );
  687. } );
  688. describe( 'should provide debug tools for delta transformation', () => {
  689. let document, root, otherRoot;
  690. beforeEach( () => {
  691. document = new ModelDocument();
  692. root = document.createRoot();
  693. otherRoot = document.createRoot( 'other', 'other' );
  694. } );
  695. it( 'Delta#_saveHistory()', () => {
  696. const insertDeltaA = new InsertDelta();
  697. const insertOpA = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
  698. insertDeltaA.addOperation( insertOpA );
  699. const insertDeltaB = new InsertDelta();
  700. const insertOpB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
  701. insertDeltaB.addOperation( insertOpB );
  702. const insertDeltaFinalA = new InsertDelta();
  703. const insertOpFinalA = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 1 );
  704. insertDeltaFinalA.addOperation( insertOpFinalA );
  705. const insertDeltaFinalAJsonWithoutHistory = JSON.stringify( insertDeltaFinalA );
  706. insertDeltaFinalA._saveHistory( {
  707. before: insertDeltaA,
  708. transformedBy: insertDeltaB,
  709. wasImportant: true,
  710. resultIndex: 0,
  711. resultsTotal: 1
  712. } );
  713. const insertDeltaC = new InsertDelta();
  714. const insertOpC = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 1 );
  715. insertDeltaC.addOperation( insertOpC );
  716. const insertDeltaFinalB = new InsertDelta();
  717. const insertOpFinalB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 2 );
  718. insertDeltaFinalB.addOperation( insertOpFinalB );
  719. insertDeltaFinalB._saveHistory( {
  720. before: insertDeltaFinalA,
  721. transformedBy: insertDeltaC,
  722. wasImportant: false,
  723. resultIndex: 1,
  724. resultsTotal: 3
  725. } );
  726. expect( insertDeltaA.history ).to.be.undefined;
  727. expect( insertDeltaB.history ).to.be.undefined;
  728. expect( insertDeltaFinalA.history ).not.to.be.undefined;
  729. expect( insertDeltaFinalA.history.length ).to.equal( 1 );
  730. expect( insertDeltaFinalA.history[ 0 ] ).to.deep.equal( {
  731. before: JSON.stringify( insertDeltaA ),
  732. transformedBy: JSON.stringify( insertDeltaB ),
  733. wasImportant: true,
  734. resultIndex: 0,
  735. resultsTotal: 1
  736. } );
  737. expect( insertDeltaFinalB.history ).not.to.be.undefined;
  738. expect( insertDeltaFinalB.history.length ).to.equal( 2 );
  739. expect( insertDeltaFinalB.history[ 0 ] ).to.deep.equal( {
  740. before: JSON.stringify( insertDeltaA ),
  741. transformedBy: JSON.stringify( insertDeltaB ),
  742. wasImportant: true,
  743. resultIndex: 0,
  744. resultsTotal: 1
  745. } );
  746. expect( insertDeltaFinalB.history[ 1 ] ).to.deep.equal( {
  747. before: insertDeltaFinalAJsonWithoutHistory,
  748. transformedBy: JSON.stringify( insertDeltaC ),
  749. wasImportant: false,
  750. resultIndex: 1,
  751. resultsTotal: 3
  752. } );
  753. } );
  754. it( 'save delta history on deltaTransform.transform', () => {
  755. const deltaA = new MoveDelta();
  756. const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
  757. deltaA.addOperation( opA );
  758. const deltaB = new InsertDelta();
  759. const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
  760. deltaB.addOperation( opB );
  761. const deltaC = new MoveDelta();
  762. const opC = new MoveOperation( ModelPosition.createAt( root, 4 ), 2, ModelPosition.createAt( root, 0 ), 1 );
  763. deltaC.addOperation( opC );
  764. let result = deltaTransform.transform( deltaA, deltaB, { document, wasAffected: new Map() } );
  765. expect( result[ 0 ].history ).not.to.be.undefined;
  766. expect( result[ 0 ].history.length ).to.equal( 1 );
  767. expect( result[ 0 ].history[ 0 ] ).to.deep.equal( {
  768. before: JSON.stringify( deltaA ),
  769. transformedBy: JSON.stringify( deltaB ),
  770. wasImportant: false,
  771. resultIndex: 0,
  772. resultsTotal: 1
  773. } );
  774. const firstResultWithoutHistory = result[ 0 ].clone();
  775. delete firstResultWithoutHistory.history;
  776. result = deltaTransform.transform( result[ 0 ], deltaC, { isStrong: true, document, wasAffected: new Map() } );
  777. expect( result[ 0 ].history ).not.to.be.undefined;
  778. expect( result[ 0 ].history.length ).to.equal( 2 );
  779. expect( result[ 0 ].history[ 0 ] ).to.deep.equal( {
  780. before: JSON.stringify( deltaA ),
  781. transformedBy: JSON.stringify( deltaB ),
  782. wasImportant: false,
  783. resultIndex: 0,
  784. resultsTotal: 1
  785. } );
  786. expect( result[ 0 ].history[ 1 ] ).to.deep.equal( {
  787. before: JSON.stringify( firstResultWithoutHistory ),
  788. transformedBy: JSON.stringify( deltaC ),
  789. wasImportant: true,
  790. resultIndex: 0,
  791. resultsTotal: 2
  792. } );
  793. } );
  794. it( 'recreate delta using Delta#history', () => {
  795. const deltaA = new MoveDelta();
  796. const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
  797. deltaA.addOperation( opA );
  798. const deltaB = new InsertDelta();
  799. const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
  800. deltaB.addOperation( opB );
  801. const deltaC = new MoveDelta();
  802. const opC = new MoveOperation( ModelPosition.createAt( root, 4 ), 2, ModelPosition.createAt( root, 0 ), 1 );
  803. deltaC.addOperation( opC );
  804. let original = deltaTransform.transform( deltaA, deltaB, { document, wasAffected: new Map() } );
  805. original = deltaTransform.transform( original[ 0 ], deltaC, { isStrong: true, document, wasAffected: new Map() } )[ 0 ];
  806. const history = original.history;
  807. let recreated = deltaTransform.transform(
  808. DeltaFactory.fromJSON( JSON.parse( history[ 0 ].before ), document ),
  809. DeltaFactory.fromJSON( JSON.parse( history[ 0 ].transformedBy ), document ),
  810. { isStrong: history[ 0 ].wasImportant, document, wasAffected: new Map() }
  811. );
  812. recreated = recreated[ history[ 0 ].resultIndex ];
  813. recreated = deltaTransform.transform(
  814. recreated,
  815. DeltaFactory.fromJSON( JSON.parse( history[ 1 ].transformedBy ), document ),
  816. { isStrong: history[ 1 ].wasImportant, document, wasAffected: new Map() }
  817. );
  818. recreated = recreated[ history[ 1 ].resultIndex ];
  819. delete original.history;
  820. delete recreated.history;
  821. expect( JSON.stringify( recreated ) ).to.equal( JSON.stringify( original ) );
  822. } );
  823. describe( 'provide additional logging when transformation crashes', () => {
  824. it( 'with more important delta A', () => {
  825. const deltaA = new MoveDelta();
  826. const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
  827. deltaA.addOperation( opA );
  828. const deltaB = new InsertDelta();
  829. const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
  830. deltaB.addOperation( opB );
  831. deltaTransform.defaultTransform = () => {
  832. throw new Error();
  833. };
  834. expect( () => deltaTransform.transform( deltaA, deltaB, { isStrong: true } ) ).to.throw( Error );
  835. expect( error.calledWith( deltaA.toString() + ' (important)' ) ).to.be.true;
  836. expect( error.calledWith( deltaB.toString() ) ).to.be.true;
  837. } );
  838. it( 'with more important delta B', () => {
  839. const deltaA = new MoveDelta();
  840. const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
  841. deltaA.addOperation( opA );
  842. const deltaB = new InsertDelta();
  843. const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
  844. deltaB.addOperation( opB );
  845. deltaTransform.defaultTransform = () => {
  846. throw new Error();
  847. };
  848. expect( () => deltaTransform.transform( deltaA, deltaB, { isStrong: false } ) ).to.throw( Error );
  849. expect( error.calledWith( deltaA.toString() ) ).to.be.true;
  850. expect( error.calledWith( deltaB.toString() + ' (important)' ) ).to.be.true;
  851. } );
  852. } );
  853. } );
  854. function expectLog( expectedLogMsg ) {
  855. expect( log.calledWithExactly( expectedLogMsg ) ).to.be.true;
  856. log.reset();
  857. }
  858. function wrapInDelta( op ) {
  859. const delta = new Delta();
  860. delta.addOperation( op );
  861. return op;
  862. }
  863. } );