enableenginedebug.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  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 ModelElement from '../../src/model/element';
  12. import AttributeOperation from '../../src/model/operation/attributeoperation';
  13. import InsertOperation from '../../src/model/operation/insertoperation';
  14. import MarkerOperation from '../../src/model/operation/markeroperation';
  15. import MoveOperation from '../../src/model/operation/moveoperation';
  16. import NoOperation from '../../src/model/operation/nooperation';
  17. import RenameOperation from '../../src/model/operation/renameoperation';
  18. import RootAttributeOperation from '../../src/model/operation/rootattributeoperation';
  19. import RemoveOperation from '../../src/model/operation/removeoperation';
  20. import Delta from '../../src/model/delta/delta';
  21. import AttributeDelta from '../../src/model/delta/attributedelta';
  22. import { RootAttributeDelta } from '../../src/model/delta/attributedelta';
  23. import InsertDelta from '../../src/model/delta/insertdelta';
  24. import MarkerDelta from '../../src/model/delta/markerdelta';
  25. import MergeDelta from '../../src/model/delta/mergedelta';
  26. import MoveDelta from '../../src/model/delta/movedelta';
  27. import RenameDelta from '../../src/model/delta/renamedelta';
  28. import SplitDelta from '../../src/model/delta/splitdelta';
  29. import UnwrapDelta from '../../src/model/delta/unwrapdelta';
  30. import WrapDelta from '../../src/model/delta/wrapdelta';
  31. import ModelDocument from '../../src/model/document';
  32. import ModelDocumentFragment from '../../src/model/documentfragment';
  33. import ViewDocument from '../../src/view/document';
  34. import ViewAttributeElement from '../../src/view/attributeelement';
  35. import ViewContainerElement from '../../src/view/containerelement';
  36. import ViewText from '../../src/view/text';
  37. import ViewDocumentFragment from '../../src/view/documentfragment';
  38. /* global document */
  39. describe( 'enableEngineDebug', () => {
  40. it( 'should return plugin class', () => {
  41. const result = enableEngineDebug();
  42. expect( result.prototype ).to.be.instanceof( Plugin );
  43. } );
  44. it( 'should not throw when called multiple times', () => {
  45. enableEngineDebug();
  46. enableEngineDebug();
  47. const result = enableEngineDebug();
  48. expect( result.prototype ).to.be.instanceof( Plugin );
  49. } );
  50. } );
  51. describe( 'debug tools', () => {
  52. let DebugPlugin, log;
  53. class TestEditor extends StandardEditor {
  54. constructor( ...args ) {
  55. super( ...args );
  56. this.document.createRoot( 'main' );
  57. this.editing.createRoot( this.element, 'main' );
  58. }
  59. }
  60. before( () => {
  61. log = sinon.spy();
  62. DebugPlugin = enableEngineDebug( log );
  63. } );
  64. afterEach( () => {
  65. log.reset();
  66. } );
  67. describe( 'should provide logging tools', () => {
  68. let modelDoc, modelRoot, modelElement, modelDocFrag;
  69. beforeEach( () => {
  70. modelDoc = new ModelDocument();
  71. modelRoot = modelDoc.createRoot();
  72. modelElement = new ModelElement( 'paragraph', null, new ModelText( 'foo' ) );
  73. modelDocFrag = new ModelDocumentFragment( [ new ModelText( 'bar' ) ] );
  74. } );
  75. it( 'for ModelText', () => {
  76. const foo = new ModelText( 'foo', { foo: 'bar' } );
  77. expect( foo.toString() ).to.equal( '#foo' );
  78. foo.log();
  79. expect( log.calledWithExactly( 'ModelText: #foo' ) ).to.be.true;
  80. foo.logExtended();
  81. expect( log.calledWithExactly( 'ModelText: #foo, attrs: {"foo":"bar"}' ) ).to.be.true;
  82. } );
  83. it( 'for ModelElement', () => {
  84. const paragraph = new ModelElement( 'paragraph', { foo: 'bar' }, new ModelText( 'foo' ) );
  85. expect( paragraph.toString() ).to.equal( '<paragraph>' );
  86. paragraph.log();
  87. expectLog( 'ModelElement: <paragraph>' );
  88. paragraph.logExtended();
  89. expectLog( 'ModelElement: <paragraph>, 1 children, attrs: {"foo":"bar"}' );
  90. sinon.spy( paragraph, 'logExtended' );
  91. paragraph.logAll();
  92. expect( paragraph.logExtended.called ).to.be.true;
  93. expectLog( 'ModelText: #foo' );
  94. } );
  95. it( 'for ModelRootElement', () => {
  96. modelRoot.log();
  97. expectLog( 'ModelRootElement: main' );
  98. } );
  99. it( 'for ModelDocumentFragment', () => {
  100. modelDocFrag.log();
  101. expectLog( 'ModelDocumentFragment: documentFragment' );
  102. } );
  103. it( 'for ModelPosition', () => {
  104. const posInRoot = new ModelPosition( modelRoot, [ 0, 1, 0 ] );
  105. const posInElement = new ModelPosition( modelElement, [ 0 ] );
  106. const posInDocFrag = new ModelPosition( modelDocFrag, [ 2, 3 ] );
  107. expect( posInRoot.toString() ).to.equal( 'main [ 0, 1, 0 ]' );
  108. expect( posInElement.toString() ).to.equal( '<paragraph> [ 0 ]' );
  109. expect( posInDocFrag.toString() ).to.equal( 'documentFragment [ 2, 3 ]' );
  110. posInRoot.log();
  111. expectLog( 'ModelPosition: main [ 0, 1, 0 ]' );
  112. } );
  113. it( 'for ModelRange', () => {
  114. const rangeInRoot = ModelRange.createIn( modelRoot );
  115. const rangeInElement = ModelRange.createIn( modelElement );
  116. const rangeInDocFrag = ModelRange.createIn( modelDocFrag );
  117. expect( rangeInRoot.toString() ).to.equal( 'main [ 0 ] - [ 0 ]' );
  118. expect( rangeInElement.toString() ).to.equal( '<paragraph> [ 0 ] - [ 3 ]' );
  119. expect( rangeInDocFrag.toString() ).to.equal( 'documentFragment [ 0 ] - [ 3 ]' );
  120. rangeInRoot.log();
  121. expectLog( 'ModelRange: main [ 0 ] - [ 0 ]' );
  122. } );
  123. describe( 'for operations', () => {
  124. beforeEach( () => {
  125. modelRoot.appendChildren( [ new ModelText( 'foobar' ) ] );
  126. } );
  127. it( 'AttributeOperation', () => {
  128. const op = new AttributeOperation( ModelRange.createIn( modelRoot ), 'key', null, { foo: 'bar' }, 0 );
  129. expect( op.toString() ).to.equal( 'AttributeOperation: "key": null -> {"foo":"bar"}, main [ 0 ] - [ 6 ]' );
  130. op.log();
  131. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  132. } );
  133. it( 'InsertOperation', () => {
  134. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelText( 'abc' ) ], 0 );
  135. expect( op.toString() ).to.equal( 'InsertOperation: [ 1 ] -> main [ 3 ]' );
  136. op.log();
  137. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  138. } );
  139. it( 'MarkerOperation', () => {
  140. const op = new MarkerOperation( 'marker', null, ModelRange.createIn( modelRoot ), modelDoc.markers, 0 );
  141. expect( op.toString() ).to.equal( 'MarkerOperation: "marker": null -> main [ 0 ] - [ 6 ]' );
  142. op.log();
  143. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  144. } );
  145. it( 'MoveOperation', () => {
  146. const op = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, ModelPosition.createAt( modelRoot, 6 ), 0 );
  147. expect( op.toString() ).to.equal( 'MoveOperation: main [ 1 ] - [ 3 ] -> main [ 6 ]' );
  148. op.log();
  149. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  150. } );
  151. it( 'NoOperation', () => {
  152. const op = new NoOperation( 0 );
  153. expect( op.toString() ).to.equal( 'NoOperation' );
  154. op.log();
  155. expect( log.calledWithExactly( 'NoOperation' ) ).to.be.true;
  156. } );
  157. it( 'RenameOperation', () => {
  158. const op = new RenameOperation( ModelPosition.createAt( modelRoot, 1 ), 'old', 'new', 0 );
  159. expect( op.toString() ).to.equal( 'RenameOperation: main [ 1 ]: "old" -> "new"' );
  160. op.log();
  161. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  162. } );
  163. it( 'RootAttributeOperation', () => {
  164. const op = new RootAttributeOperation( modelRoot, 'key', 'old', null, 0 );
  165. expect( op.toString() ).to.equal( 'RootAttributeOperation: "key": "old" -> null, main' );
  166. op.log();
  167. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  168. } );
  169. } );
  170. describe( 'for deltas', () => {
  171. it( 'Delta', () => {
  172. const delta = new Delta();
  173. const op = { log: sinon.spy() };
  174. delta.addOperation( op );
  175. sinon.spy( delta, 'log' );
  176. delta.logAll();
  177. expect( op.log.called ).to.be.true;
  178. expect( delta.log.called ).to.be.true;
  179. } );
  180. it( 'AttributeDelta', () => {
  181. modelRoot.appendChildren( new ModelText( 'foobar' ) );
  182. const delta = new AttributeDelta();
  183. const op = new AttributeOperation( ModelRange.createIn( modelRoot ), 'key', null, { foo: 'bar' }, 0 );
  184. delta.addOperation( op );
  185. expect( delta.toString() ).to.equal( 'AttributeDelta: "key": -> {"foo":"bar"}, main [ 0 ] - [ 6 ], 1 ops' );
  186. delta.log();
  187. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  188. } );
  189. it( 'InsertDelta', () => {
  190. const delta = new InsertDelta();
  191. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelText( 'abc' ) ], 0 );
  192. delta.addOperation( op );
  193. expect( delta.toString() ).to.equal( 'InsertDelta: [ 1 ] -> main [ 3 ]' );
  194. delta.log();
  195. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  196. } );
  197. it( 'MarkerDelta', () => {
  198. modelRoot.appendChildren( new ModelText( 'foobar' ) );
  199. const delta = new MarkerDelta();
  200. const op = new MarkerOperation( 'marker', null, ModelRange.createIn( modelRoot ), modelDoc.markers, 0 );
  201. delta.addOperation( op );
  202. expect( delta.toString() ).to.equal( 'MarkerDelta: "marker": null -> main [ 0 ] - [ 6 ]' );
  203. delta.log();
  204. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  205. } );
  206. it( 'MergeDelta', () => {
  207. const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
  208. const firstEle = new ModelElement( 'paragraph' );
  209. const removedEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
  210. otherRoot.appendChildren( [ firstEle, removedEle ] );
  211. const delta = new MergeDelta();
  212. const move = new MoveOperation( ModelPosition.createAt( removedEle, 0 ), 3, ModelPosition.createAt( firstEle, 0 ), 0 );
  213. const remove = new RemoveOperation( ModelPosition.createBefore( removedEle ), 1, 1 );
  214. delta.addOperation( move );
  215. delta.addOperation( remove );
  216. expect( delta.toString() ).to.equal( 'MergeDelta: otherRoot [ 1 ]' );
  217. delta.log();
  218. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  219. } );
  220. it( 'MoveDelta', () => {
  221. const delta = new MoveDelta();
  222. const move1 = new MoveOperation( ModelPosition.createAt( modelRoot, 0 ), 1, ModelPosition.createAt( modelRoot, 3 ), 0 );
  223. const move2 = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 1, ModelPosition.createAt( modelRoot, 6 ), 0 );
  224. delta.addOperation( move1 );
  225. delta.addOperation( move2 );
  226. expect( delta.toString() ).to.equal( 'MoveDelta: main [ 0 ] - [ 1 ] -> main [ 3 ]; main [ 1 ] - [ 2 ] -> main [ 6 ]' );
  227. delta.log();
  228. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  229. } );
  230. it( 'RenameDelta', () => {
  231. const delta = new RenameDelta();
  232. const op = new RenameOperation( ModelPosition.createAt( modelRoot, 1 ), 'old', 'new', 0 );
  233. delta.addOperation( op );
  234. expect( delta.toString() ).to.equal( 'RenameDelta: main [ 1 ]: "old" -> "new"' );
  235. delta.log();
  236. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  237. } );
  238. it( 'RootAttributeDelta', () => {
  239. const delta = new RootAttributeDelta();
  240. const op = new RootAttributeOperation( modelRoot, 'key', 'old', null, 0 );
  241. delta.addOperation( op );
  242. expect( delta.toString() ).to.equal( 'RootAttributeDelta: "key": "old" -> null, main' );
  243. delta.log();
  244. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  245. } );
  246. it( 'SplitDelta', () => {
  247. const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
  248. const splitEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
  249. otherRoot.appendChildren( [ splitEle ] );
  250. const delta = new SplitDelta();
  251. const insert = new InsertOperation( ModelPosition.createAt( otherRoot, 1 ), [ new ModelElement( 'paragraph' ) ], 0 );
  252. const move = new MoveOperation( ModelPosition.createAt( splitEle, 1 ), 2, new ModelPosition( otherRoot, [ 1, 0 ] ), 1 );
  253. delta.addOperation( insert );
  254. delta.addOperation( move );
  255. expect( delta.toString() ).to.equal( 'SplitDelta: otherRoot [ 0, 1 ]' );
  256. delta.log();
  257. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  258. } );
  259. it( 'UnwrapDelta', () => {
  260. const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
  261. const unwrapEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
  262. otherRoot.appendChildren( [ unwrapEle ] );
  263. const delta = new UnwrapDelta();
  264. const move = new MoveOperation( ModelPosition.createAt( unwrapEle, 0 ), 3, ModelPosition.createAt( otherRoot, 0 ), 1 );
  265. const remove = new RemoveOperation( ModelPosition.createAt( otherRoot, 3 ), 1, 0 );
  266. delta.addOperation( move );
  267. delta.addOperation( remove );
  268. expect( delta.toString() ).to.equal( 'UnwrapDelta: otherRoot [ 0 ]' );
  269. delta.log();
  270. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  271. } );
  272. it( 'WrapDelta', () => {
  273. const delta = new WrapDelta();
  274. const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 6 ), new ModelElement( 'paragraph' ), 0 );
  275. const move = new MoveOperation( ModelPosition.createAt( modelRoot, 0 ), 6, new ModelPosition( modelRoot, [ 1, 0 ] ), 1 );
  276. delta.addOperation( insert );
  277. delta.addOperation( move );
  278. expect( delta.toString() ).to.equal( 'WrapDelta: main [ 0 ] - [ 6 ] -> <paragraph>' );
  279. delta.log();
  280. expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
  281. } );
  282. } );
  283. it( 'for applied operations', () => {
  284. const delta = new InsertDelta();
  285. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), [ new ModelText( 'foo' ) ], 0 );
  286. delta.addOperation( op );
  287. modelDoc.applyOperation( op );
  288. expect( log.calledWithExactly( 'Applying InsertOperation: [ 1 ] -> main [ 0 ]' ) ).to.be.true;
  289. } );
  290. } );
  291. describe( 'should provide tree printing tools', () => {
  292. it( 'for model', () => {
  293. const modelDoc = new ModelDocument();
  294. const modelRoot = modelDoc.createRoot();
  295. modelRoot.appendChildren( [
  296. new ModelElement( 'paragraph', { foo: 'bar' }, [
  297. new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' )
  298. ] ),
  299. new ModelElement( 'listItem', { type: 'numbered', indent: 0 }, new ModelText( 'One.' ) ),
  300. ] );
  301. const modelRootTree = modelRoot.printTree();
  302. expect( modelRootTree ).to.equal(
  303. '<main>' +
  304. '\n\t<paragraph foo="bar">' +
  305. '\n\t\tThis is ' +
  306. '\n\t\t<$text bold=true>bold</$text>' +
  307. '\n\t\t.' +
  308. '\n\t</paragraph>' +
  309. '\n\t<listItem type="numbered" indent=0>' +
  310. '\n\t\tOne.' +
  311. '\n\t</listItem>' +
  312. '\n</main>'
  313. );
  314. modelRoot.logTree();
  315. expect( log.calledWithExactly( modelRootTree ) ).to.be.true;
  316. const modelParagraph = modelRoot.getChild( 0 );
  317. const modelParagraphTree = modelParagraph.printTree();
  318. expect( modelParagraphTree ).to.equal(
  319. '<paragraph foo="bar">' +
  320. '\n\tThis is ' +
  321. '\n\t<$text bold=true>bold</$text>' +
  322. '\n\t.' +
  323. '\n</paragraph>'
  324. );
  325. log.reset();
  326. modelParagraph.logTree();
  327. expect( log.calledWithExactly( modelParagraphTree ) ).to.be.true;
  328. const modelDocFrag = new ModelDocumentFragment( [
  329. new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' ),
  330. new ModelElement( 'paragraph', { foo: 'bar' }, [
  331. new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' )
  332. ] )
  333. ] );
  334. const modelDocFragTree = modelDocFrag.printTree();
  335. expect( modelDocFragTree ).to.equal(
  336. 'ModelDocumentFragment: [' +
  337. '\n\tThis is ' +
  338. '\n\t<$text bold=true>bold</$text>' +
  339. '\n\t.' +
  340. '\n\t<paragraph foo="bar">' +
  341. '\n\t\tThis is ' +
  342. '\n\t\t<$text bold=true>bold</$text>' +
  343. '\n\t\t.' +
  344. '\n\t</paragraph>' +
  345. '\n]'
  346. );
  347. log.reset();
  348. modelDocFrag.logTree();
  349. expect( log.calledWithExactly( modelDocFragTree ) ).to.be.true;
  350. } );
  351. it( 'for view', () => {
  352. const viewDoc = new ViewDocument();
  353. const viewRoot = viewDoc.createRoot( 'div' );
  354. viewRoot.appendChildren( [
  355. new ViewContainerElement( 'p', { foo: 'bar' }, [
  356. new ViewText( 'This is ' ), new ViewAttributeElement( 'b', null, new ViewText( 'bold' ) ), new ViewText( '.' )
  357. ] ),
  358. new ViewContainerElement( 'ol', null, [
  359. new ViewContainerElement( 'li', null, new ViewText( 'One.' ) )
  360. ] )
  361. ] );
  362. const viewRootTree = viewRoot.printTree();
  363. expect( viewRootTree ).to.equal(
  364. '<div>' +
  365. '\n\t<p foo="bar">' +
  366. '\n\t\tThis is ' +
  367. '\n\t\t<b>' +
  368. '\n\t\t\tbold' +
  369. '\n\t\t</b>' +
  370. '\n\t\t.' +
  371. '\n\t</p>' +
  372. '\n\t<ol>' +
  373. '\n\t\t<li>' +
  374. '\n\t\t\tOne.' +
  375. '\n\t\t</li>' +
  376. '\n\t</ol>' +
  377. '\n</div>'
  378. );
  379. viewRoot.logTree();
  380. expect( log.calledWithExactly( viewRootTree ) ).to.be.true;
  381. const viewParagraph = viewRoot.getChild( 0 );
  382. const viewParagraphTree = viewParagraph.printTree();
  383. expect( viewParagraphTree ).to.equal(
  384. '<p foo="bar">' +
  385. '\n\tThis is ' +
  386. '\n\t<b>' +
  387. '\n\t\tbold' +
  388. '\n\t</b>' +
  389. '\n\t.' +
  390. '\n</p>'
  391. );
  392. log.reset();
  393. viewParagraph.logTree();
  394. expect( log.calledWithExactly( viewParagraphTree ) ).to.be.true;
  395. const viewDocFrag = new ViewDocumentFragment( [
  396. new ViewText( 'Text.' ),
  397. new ViewContainerElement( 'p', { foo: 'bar' }, [
  398. new ViewText( 'This is ' ), new ViewAttributeElement( 'b', null, new ViewText( 'bold' ) ), new ViewText( '.' )
  399. ] )
  400. ] );
  401. const viewDocFragTree = viewDocFrag.printTree();
  402. expect( viewDocFragTree ).to.equal(
  403. 'ViewDocumentFragment: [' +
  404. '\n\tText.' +
  405. '\n\t<p foo="bar">' +
  406. '\n\t\tThis is ' +
  407. '\n\t\t<b>' +
  408. '\n\t\t\tbold' +
  409. '\n\t\t</b>' +
  410. '\n\t\t.' +
  411. '\n\t</p>' +
  412. '\n]'
  413. );
  414. log.reset();
  415. viewDocFrag.logTree();
  416. expect( log.calledWithExactly( viewDocFragTree ) ).to.be.true;
  417. } );
  418. } );
  419. describe( 'should store model and view trees state', () => {
  420. let editor;
  421. beforeEach( () => {
  422. const div = document.createElement( 'div' );
  423. return TestEditor.create( div, {
  424. plugins: [ DebugPlugin ]
  425. } ).then( ( _editor ) => {
  426. editor = _editor;
  427. } );
  428. } );
  429. it( 'should store model and view state after each applied operation', () => {
  430. const model = editor.document;
  431. const modelRoot = model.getRoot();
  432. const view = editor.editing.view;
  433. const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), new ModelText( 'foobar' ), 0 );
  434. model.applyOperation( wrapInDelta( insert ) );
  435. const remove = new RemoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, 1 );
  436. model.applyOperation( wrapInDelta( remove ) );
  437. log.reset();
  438. model.log( 0 );
  439. expectLog(
  440. '<$graveyard></$graveyard>' +
  441. '\n<main></main>'
  442. );
  443. model.log( 1 );
  444. expectLog(
  445. '<$graveyard></$graveyard>' +
  446. '\n<main>' +
  447. '\n\tfoobar' +
  448. '\n</main>'
  449. );
  450. model.log( 2 );
  451. expectLog(
  452. '<$graveyard>' +
  453. '\n\t<$graveyardHolder>' +
  454. '\n\t\too' +
  455. '\n\t</$graveyardHolder>' +
  456. '\n</$graveyard>' +
  457. '\n<main>' +
  458. '\n\tfbar' +
  459. '\n</main>'
  460. );
  461. model.log();
  462. expectLog(
  463. '<$graveyard>' +
  464. '\n\t<$graveyardHolder>' +
  465. '\n\t\too' +
  466. '\n\t</$graveyardHolder>' +
  467. '\n</$graveyard>' +
  468. '\n<main>' +
  469. '\n\tfbar' +
  470. '\n</main>'
  471. );
  472. view.log( 0 );
  473. expectLog(
  474. '<div></div>'
  475. );
  476. view.log( 1 );
  477. expectLog(
  478. '<div>' +
  479. '\n\tfoobar' +
  480. '\n</div>'
  481. );
  482. view.log( 2 );
  483. expectLog(
  484. '<div>' +
  485. '\n\tfbar' +
  486. '\n</div>'
  487. );
  488. sinon.spy( model, 'log' );
  489. sinon.spy( view, 'log' );
  490. editor.logModel( 1 );
  491. expect( model.log.calledWithExactly( 1 ) ).to.be.true;
  492. editor.logView( 2 );
  493. expect( view.log.calledWithExactly( 2 ) ).to.be.true;
  494. model.log.reset();
  495. view.log.reset();
  496. editor.logModel();
  497. expect( model.log.calledWithExactly( 2 ) ).to.be.true;
  498. model.log.reset();
  499. view.log.reset();
  500. editor.logDocuments();
  501. expect( model.log.calledWithExactly( 2 ) ).to.be.true;
  502. expect( view.log.calledWithExactly( 2 ) ).to.be.true;
  503. model.log.reset();
  504. view.log.reset();
  505. editor.logDocuments( 1 );
  506. expect( model.log.calledWithExactly( 1 ) ).to.be.true;
  507. expect( view.log.calledWithExactly( 1 ) ).to.be.true;
  508. } );
  509. it( 'should remove old states', () => {
  510. const model = editor.document;
  511. const modelRoot = model.getRoot();
  512. for ( let i = 0; i < 25; i++ ) {
  513. const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), new ModelText( 'foobar' ), model.version );
  514. model.applyOperation( wrapInDelta( insert ) );
  515. }
  516. model.log( 0 );
  517. expectLog( 'Tree log unavailable for given version: 0' );
  518. } );
  519. } );
  520. function expectLog( expectedLogMsg ) {
  521. expect( log.calledWithExactly( expectedLogMsg ) ).to.be.true;
  522. log.reset();
  523. }
  524. } );
  525. function wrapInDelta( op ) {
  526. const delta = new Delta();
  527. delta.addOperation( op );
  528. return op;
  529. }