8
0

enableenginedebug.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import { default as enableEngineDebug, disableEngineDebug } from '../../src/dev-utils/enableenginedebug';
  6. import Editor from '@ckeditor/ckeditor5-core/src/editor/editor';
  7. import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
  8. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  9. import ModelPosition from '../../src/model/position';
  10. import ModelRange from '../../src/model/range';
  11. import ModelText from '../../src/model/text';
  12. import ModelTextProxy from '../../src/model/textproxy';
  13. import ModelElement from '../../src/model/element';
  14. import AttributeOperation from '../../src/model/operation/attributeoperation';
  15. import DetachOperation from '../../src/model/operation/detachoperation';
  16. import InsertOperation from '../../src/model/operation/insertoperation';
  17. import MarkerOperation from '../../src/model/operation/markeroperation';
  18. import MoveOperation from '../../src/model/operation/moveoperation';
  19. import SplitOperation from '../../src/model/operation/splitoperation';
  20. import MergeOperation from '../../src/model/operation/mergeoperation';
  21. import WrapOperation from '../../src/model/operation/wrapoperation';
  22. import UnwrapOperation from '../../src/model/operation/unwrapoperation';
  23. import NoOperation from '../../src/model/operation/nooperation';
  24. import RenameOperation from '../../src/model/operation/renameoperation';
  25. import RootAttributeOperation from '../../src/model/operation/rootattributeoperation';
  26. import RemoveOperation from '../../src/model/operation/removeoperation';
  27. import transform from '../../src/model/operation/transform';
  28. import Model from '../../src/model/model';
  29. import ModelDocumentFragment from '../../src/model/documentfragment';
  30. import ViewDocument from '../../src/view/document';
  31. import ViewAttributeElement from '../../src/view/attributeelement';
  32. import ViewContainerElement from '../../src/view/containerelement';
  33. import ViewText from '../../src/view/text';
  34. import ViewTextProxy from '../../src/view/textproxy';
  35. import ViewDocumentFragment from '../../src/view/documentfragment';
  36. import ViewElement from '../../src/view/element';
  37. import createViewRoot from '../view/_utils/createroot';
  38. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  39. describe( 'enableEngineDebug', () => {
  40. testUtils.createSinonSandbox();
  41. afterEach( () => {
  42. disableEngineDebug();
  43. } );
  44. it( 'should return plugin class', () => {
  45. const result = enableEngineDebug();
  46. expect( result.prototype ).to.be.instanceof( Plugin );
  47. } );
  48. it( 'should not throw when called multiple times', () => {
  49. enableEngineDebug();
  50. enableEngineDebug();
  51. const result = enableEngineDebug();
  52. expect( result.prototype ).to.be.instanceof( Plugin );
  53. } );
  54. } );
  55. describe( 'disableEngineDebug', () => {
  56. testUtils.createSinonSandbox();
  57. it( 'restores modified stubs', () => {
  58. expect( ModelPosition.prototype.log ).to.equal( undefined, 'Initial value (model/position)' );
  59. expect( ModelElement.prototype.printTree ).to.equal( undefined, 'Initial value (model/element)' );
  60. expect( ViewElement.prototype.printTree ).to.equal( undefined, 'Initial value (view/element)' );
  61. expect( Model.prototype.createReplayer ).to.equal( undefined, 'Initial value (model/document)' );
  62. expect( Editor.prototype.logDocuments ).to.equal( undefined, 'Initial value (core~editor/editor)' );
  63. enableEngineDebug();
  64. expect( ModelPosition.prototype.log ).to.be.a( 'function', 'After enabling engine debug (model/position)' );
  65. expect( ModelElement.prototype.printTree ).to.be.a( 'function', 'After enabling engine debug (model/element)' );
  66. expect( ViewElement.prototype.printTree ).to.be.a( 'function', 'After enabling engine debug (view/element)' );
  67. expect( Model.prototype.createReplayer ).to.be.a( 'function', 'After enabling engine debug (model/document)' );
  68. expect( Editor.prototype.logDocuments ).to.be.a( 'function', 'After enabling engine debug (core~editor/editor)' );
  69. disableEngineDebug();
  70. expect( ModelPosition.prototype.log ).to.equal( undefined, 'After disabling engine debug (model/position)' );
  71. expect( ModelElement.prototype.printTree ).to.equal( undefined, 'After disabling engine debug (model/element)' );
  72. expect( ViewElement.prototype.printTree ).to.equal( undefined, 'After disabling engine debug (view/element)' );
  73. expect( Model.prototype.createReplayer ).to.equal( undefined, 'After disabling engine debug (model/document)' );
  74. expect( Editor.prototype.logDocuments ).to.equal( undefined, 'After disabling engine debug (core~editor/editor)' );
  75. } );
  76. } );
  77. describe( 'debug tools', () => {
  78. let DebugPlugin, log, error;
  79. testUtils.createSinonSandbox();
  80. before( () => {
  81. log = sinon.spy();
  82. error = sinon.spy();
  83. DebugPlugin = enableEngineDebug( { log, error } );
  84. } );
  85. after( () => {
  86. disableEngineDebug();
  87. } );
  88. afterEach( () => {
  89. log.resetHistory();
  90. } );
  91. describe( 'should provide logging tools', () => {
  92. let model, modelDoc, modelRoot, modelElement, modelDocFrag;
  93. beforeEach( () => {
  94. model = new Model();
  95. modelDoc = model.document;
  96. modelRoot = modelDoc.createRoot();
  97. modelElement = new ModelElement( 'paragraph', null, new ModelText( 'foo' ) );
  98. modelDocFrag = new ModelDocumentFragment( [ new ModelText( 'bar' ) ] );
  99. } );
  100. it( 'for ModelText', () => {
  101. const foo = new ModelText( 'foo', { foo: 'bar' } );
  102. expect( foo.toString() ).to.equal( '#foo' );
  103. foo.log();
  104. expect( log.calledWithExactly( 'ModelText: #foo' ) ).to.be.true;
  105. foo.logExtended();
  106. expect( log.calledWithExactly( 'ModelText: #foo, attrs: {"foo":"bar"}' ) ).to.be.true;
  107. } );
  108. it( 'for ModelTextProxy', () => {
  109. const foo = new ModelText( 'foo', { foo: 'bar' } );
  110. const proxy = new ModelTextProxy( foo, 1, 1 );
  111. expect( proxy.toString() ).to.equal( '#o' );
  112. proxy.log();
  113. expect( log.calledWithExactly( 'ModelTextProxy: #o' ) ).to.be.true;
  114. proxy.logExtended();
  115. expect( log.calledWithExactly( 'ModelTextProxy: #o, attrs: {"foo":"bar"}' ) ).to.be.true;
  116. } );
  117. it( 'for ModelElement', () => {
  118. const paragraph = new ModelElement( 'paragraph', { foo: 'bar' }, new ModelText( 'foo' ) );
  119. expect( paragraph.toString() ).to.equal( '<paragraph>' );
  120. paragraph.log();
  121. expectLog( 'ModelElement: <paragraph>' );
  122. paragraph.logExtended();
  123. expectLog( 'ModelElement: <paragraph>, 1 children, attrs: {"foo":"bar"}' );
  124. sinon.spy( paragraph, 'logExtended' );
  125. paragraph.logAll();
  126. expect( paragraph.logExtended.called ).to.be.true;
  127. expectLog( 'ModelText: #foo' );
  128. } );
  129. it( 'for ModelRootElement', () => {
  130. modelRoot.log();
  131. expectLog( 'ModelRootElement: main' );
  132. } );
  133. it( 'for ModelDocumentFragment', () => {
  134. modelDocFrag.log();
  135. expectLog( 'ModelDocumentFragment: documentFragment' );
  136. } );
  137. it( 'for ModelPosition', () => {
  138. const posInRoot = new ModelPosition( modelRoot, [ 0, 1, 0 ] );
  139. const posInElement = new ModelPosition( modelElement, [ 0 ] );
  140. const posInDocFrag = new ModelPosition( modelDocFrag, [ 2, 3 ] );
  141. expect( posInRoot.toString() ).to.equal( 'main [ 0, 1, 0 ]' );
  142. expect( posInElement.toString() ).to.equal( '<paragraph> [ 0 ]' );
  143. expect( posInDocFrag.toString() ).to.equal( 'documentFragment [ 2, 3 ]' );
  144. posInRoot.log();
  145. expectLog( 'ModelPosition: main [ 0, 1, 0 ]' );
  146. } );
  147. it( 'for ModelRange', () => {
  148. const rangeInRoot = ModelRange.createIn( modelRoot );
  149. const rangeInElement = ModelRange.createIn( modelElement );
  150. const rangeInDocFrag = ModelRange.createIn( modelDocFrag );
  151. expect( rangeInRoot.toString() ).to.equal( 'main [ 0 ] - [ 0 ]' );
  152. expect( rangeInElement.toString() ).to.equal( '<paragraph> [ 0 ] - [ 3 ]' );
  153. expect( rangeInDocFrag.toString() ).to.equal( 'documentFragment [ 0 ] - [ 3 ]' );
  154. rangeInRoot.log();
  155. expectLog( 'ModelRange: main [ 0 ] - [ 0 ]' );
  156. } );
  157. it( 'for ViewText', () => {
  158. const foo = new ViewText( 'foo' );
  159. expect( foo.toString() ).to.equal( '#foo' );
  160. foo.log();
  161. expect( log.calledWithExactly( 'ViewText: #foo' ) ).to.be.true;
  162. foo.logExtended();
  163. expect( log.calledWithExactly( 'ViewText: #foo' ) ).to.be.true;
  164. } );
  165. it( 'for ViewTextProxy', () => {
  166. const foo = new ViewText( 'foo', { foo: 'bar' } );
  167. const proxy = new ViewTextProxy( foo, 1, 1 );
  168. expect( proxy.toString() ).to.equal( '#o' );
  169. proxy.log();
  170. expect( log.calledWithExactly( 'ViewTextProxy: #o' ) ).to.be.true;
  171. proxy.logExtended();
  172. expect( log.calledWithExactly( 'ViewTextProxy: #o' ) ).to.be.true;
  173. } );
  174. describe( 'for operations', () => {
  175. beforeEach( () => {
  176. modelRoot._appendChild( [ new ModelText( 'foobar' ) ] );
  177. } );
  178. it( 'AttributeOperation', () => {
  179. const op = new AttributeOperation( ModelRange.createIn( modelRoot ), 'key', null, { foo: 'bar' }, 0 );
  180. expect( op.toString() ).to.equal( 'AttributeOperation( 0 ): "key": null -> {"foo":"bar"}, main [ 0 ] - [ 6 ]' );
  181. op.log();
  182. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  183. } );
  184. it( 'DetachOperation (text node)', () => {
  185. const op = new DetachOperation( ModelPosition.createAt( modelRoot, 0 ), 3 );
  186. expect( op.toString() ).to.equal( 'DetachOperation( null ): #foo -> main [ 0 ] - [ 3 ]' );
  187. op.log();
  188. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  189. } );
  190. it( 'DetachOperation (element)', () => {
  191. const element = new ModelElement( 'element' );
  192. modelRoot._insertChild( 0, element );
  193. const op = new DetachOperation( ModelPosition.createBefore( element ), 1 );
  194. expect( op.toString() ).to.equal( 'DetachOperation( null ): <element> -> main [ 0 ] - [ 1 ]' );
  195. op.log();
  196. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  197. } );
  198. it( 'DetachOperation (multiple nodes)', () => {
  199. const element = new ModelElement( 'element' );
  200. modelRoot._insertChild( 0, element );
  201. const op = new DetachOperation( ModelPosition.createBefore( element ), 2 );
  202. expect( op.toString() ).to.equal( 'DetachOperation( null ): [ 2 ] -> main [ 0 ] - [ 2 ]' );
  203. op.log();
  204. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  205. } );
  206. it( 'InsertOperation (text node)', () => {
  207. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelText( 'abc' ) ], 0 );
  208. expect( op.toString() ).to.equal( 'InsertOperation( 0 ): #abc -> main [ 3 ]' );
  209. op.log();
  210. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  211. } );
  212. it( 'InsertOperation (element)', () => {
  213. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelElement( 'paragraph' ) ], 0 );
  214. expect( op.toString() ).to.equal( 'InsertOperation( 0 ): <paragraph> -> main [ 3 ]' );
  215. op.log();
  216. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  217. } );
  218. it( 'InsertOperation (multiple nodes)', () => {
  219. const nodes = [ new ModelText( 'x' ), new ModelElement( 'y' ), new ModelText( 'z' ) ];
  220. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), nodes, 0 );
  221. expect( op.toString() ).to.equal( 'InsertOperation( 0 ): [ 3 ] -> main [ 3 ]' );
  222. op.log();
  223. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  224. } );
  225. it( 'MarkerOperation', () => {
  226. const op = new MarkerOperation( 'marker', null, ModelRange.createIn( modelRoot ), modelDoc.markers, 0 );
  227. expect( op.toString() ).to.equal( 'MarkerOperation( 0 ): "marker": null -> main [ 0 ] - [ 6 ]' );
  228. op.log();
  229. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  230. } );
  231. it( 'MoveOperation', () => {
  232. const op = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, ModelPosition.createAt( modelRoot, 6 ), 0 );
  233. expect( op.toString() ).to.equal( 'MoveOperation( 0 ): main [ 1 ] - [ 3 ] -> main [ 6 ]' );
  234. op.log();
  235. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  236. } );
  237. it( 'MoveOperation sticky', () => {
  238. const op = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, ModelPosition.createAt( modelRoot, 6 ), 0 );
  239. op.isSticky = true;
  240. expect( op.toString() ).to.equal( 'MoveOperation( 0 ): main [ 1 ] - [ 3 ] -> main [ 6 ] (sticky)' );
  241. op.log();
  242. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  243. } );
  244. it( 'NoOperation', () => {
  245. const op = new NoOperation( 0 );
  246. expect( op.toString() ).to.equal( 'NoOperation( 0 )' );
  247. op.log();
  248. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  249. } );
  250. it( 'RenameOperation', () => {
  251. const op = new RenameOperation( ModelPosition.createAt( modelRoot, 1 ), 'old', 'new', 0 );
  252. expect( op.toString() ).to.equal( 'RenameOperation( 0 ): main [ 1 ]: "old" -> "new"' );
  253. op.log();
  254. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  255. } );
  256. it( 'RootAttributeOperation', () => {
  257. const op = new RootAttributeOperation( modelRoot, 'key', 'old', null, 0 );
  258. expect( op.toString() ).to.equal( 'RootAttributeOperation( 0 ): "key": "old" -> null, main' );
  259. op.log();
  260. expect( log.calledWithExactly( op.toString() ) ).to.be.true;
  261. } );
  262. } );
  263. it( 'for applied operations', () => {
  264. const op = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), [ new ModelText( 'foo' ) ], 0 );
  265. model.applyOperation( op );
  266. expect( log.calledWithExactly( 'Applying InsertOperation( 0 ): #foo -> main [ 0 ]' ) ).to.be.true;
  267. } );
  268. } );
  269. describe( 'should provide tree printing tools', () => {
  270. it( 'for model', () => {
  271. const model = new Model();
  272. const modelDoc = model.document;
  273. const modelRoot = modelDoc.createRoot();
  274. modelRoot._appendChild( [
  275. new ModelElement( 'paragraph', { foo: 'bar' }, [
  276. new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' )
  277. ] ),
  278. new ModelElement( 'listItem', { listType: 'numbered', listIndent: 0 }, new ModelText( 'One.' ) ),
  279. ] );
  280. const modelRootTree = modelRoot.printTree();
  281. expect( modelRootTree ).to.equal(
  282. '<main>' +
  283. '\n\t<paragraph foo="bar">' +
  284. '\n\t\tThis is ' +
  285. '\n\t\t<$text bold=true>bold</$text>' +
  286. '\n\t\t.' +
  287. '\n\t</paragraph>' +
  288. '\n\t<listItem listType="numbered" listIndent=0>' +
  289. '\n\t\tOne.' +
  290. '\n\t</listItem>' +
  291. '\n</main>'
  292. );
  293. modelRoot.logTree();
  294. expect( log.calledWithExactly( modelRootTree ) ).to.be.true;
  295. const modelParagraph = modelRoot.getChild( 0 );
  296. const modelParagraphTree = modelParagraph.printTree();
  297. expect( modelParagraphTree ).to.equal(
  298. '<paragraph foo="bar">' +
  299. '\n\tThis is ' +
  300. '\n\t<$text bold=true>bold</$text>' +
  301. '\n\t.' +
  302. '\n</paragraph>'
  303. );
  304. log.resetHistory();
  305. modelParagraph.logTree();
  306. expect( log.calledWithExactly( modelParagraphTree ) ).to.be.true;
  307. const modelDocFrag = new ModelDocumentFragment( [
  308. new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' ),
  309. new ModelElement( 'paragraph', { foo: 'bar' }, [
  310. new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' )
  311. ] )
  312. ] );
  313. const modelDocFragTree = modelDocFrag.printTree();
  314. expect( modelDocFragTree ).to.equal(
  315. 'ModelDocumentFragment: [' +
  316. '\n\tThis is ' +
  317. '\n\t<$text bold=true>bold</$text>' +
  318. '\n\t.' +
  319. '\n\t<paragraph foo="bar">' +
  320. '\n\t\tThis is ' +
  321. '\n\t\t<$text bold=true>bold</$text>' +
  322. '\n\t\t.' +
  323. '\n\t</paragraph>' +
  324. '\n]'
  325. );
  326. log.resetHistory();
  327. modelDocFrag.logTree();
  328. expect( log.calledWithExactly( modelDocFragTree ) ).to.be.true;
  329. } );
  330. it( 'for view', () => {
  331. const viewDoc = new ViewDocument();
  332. const viewRoot = createViewRoot( viewDoc );
  333. viewRoot._appendChild( [
  334. new ViewContainerElement( 'p', { foo: 'bar' }, [
  335. new ViewText( 'This is ' ), new ViewAttributeElement( 'b', null, new ViewText( 'bold' ) ), new ViewText( '.' )
  336. ] ),
  337. new ViewContainerElement( 'ol', null, [
  338. new ViewContainerElement( 'li', null, new ViewText( 'One.' ) )
  339. ] )
  340. ] );
  341. const viewRootTree = viewRoot.printTree();
  342. expect( viewRootTree ).to.equal(
  343. '<div>' +
  344. '\n\t<p foo="bar">' +
  345. '\n\t\tThis is ' +
  346. '\n\t\t<b>' +
  347. '\n\t\t\tbold' +
  348. '\n\t\t</b>' +
  349. '\n\t\t.' +
  350. '\n\t</p>' +
  351. '\n\t<ol>' +
  352. '\n\t\t<li>' +
  353. '\n\t\t\tOne.' +
  354. '\n\t\t</li>' +
  355. '\n\t</ol>' +
  356. '\n</div>'
  357. );
  358. viewRoot.logTree();
  359. expect( log.calledWithExactly( viewRootTree ) ).to.be.true;
  360. const viewParagraph = viewRoot.getChild( 0 );
  361. const viewParagraphTree = viewParagraph.printTree();
  362. expect( viewParagraphTree ).to.equal(
  363. '<p foo="bar">' +
  364. '\n\tThis is ' +
  365. '\n\t<b>' +
  366. '\n\t\tbold' +
  367. '\n\t</b>' +
  368. '\n\t.' +
  369. '\n</p>'
  370. );
  371. log.resetHistory();
  372. viewParagraph.logTree();
  373. expect( log.calledWithExactly( viewParagraphTree ) ).to.be.true;
  374. const viewDocFrag = new ViewDocumentFragment( [
  375. new ViewText( 'Text.' ),
  376. new ViewContainerElement( 'p', { foo: 'bar' }, [
  377. new ViewText( 'This is ' ), new ViewAttributeElement( 'b', null, new ViewText( 'bold' ) ), new ViewText( '.' )
  378. ] )
  379. ] );
  380. const viewDocFragTree = viewDocFrag.printTree();
  381. expect( viewDocFragTree ).to.equal(
  382. 'ViewDocumentFragment: [' +
  383. '\n\tText.' +
  384. '\n\t<p foo="bar">' +
  385. '\n\t\tThis is ' +
  386. '\n\t\t<b>' +
  387. '\n\t\t\tbold' +
  388. '\n\t\t</b>' +
  389. '\n\t\t.' +
  390. '\n\t</p>' +
  391. '\n]'
  392. );
  393. log.resetHistory();
  394. viewDocFrag.logTree();
  395. expect( log.calledWithExactly( viewDocFragTree ) ).to.be.true;
  396. } );
  397. } );
  398. describe( 'should store model and view trees state', () => {
  399. let editor;
  400. beforeEach( () => {
  401. return VirtualTestEditor.create( {
  402. plugins: [ DebugPlugin ]
  403. } ).then( _editor => {
  404. editor = _editor;
  405. } );
  406. } );
  407. it( 'should store model and view state after each applied operation', () => {
  408. const model = editor.model;
  409. const modelDoc = model.document;
  410. const modelRoot = modelDoc.getRoot();
  411. const view = editor.editing.view;
  412. const viewDoc = view.document;
  413. model.change( () => {
  414. const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), new ModelText( 'foobar' ), 0 );
  415. model.applyOperation( insert );
  416. const graveyard = modelDoc.graveyard;
  417. const remove = new RemoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, ModelPosition.createAt( graveyard, 0 ), 1 );
  418. model.applyOperation( remove );
  419. } );
  420. log.resetHistory();
  421. modelDoc.log( 0 );
  422. expectLog(
  423. '<$graveyard></$graveyard>' +
  424. '\n<main></main>'
  425. );
  426. modelDoc.log( 1 );
  427. expectLog(
  428. '<$graveyard></$graveyard>' +
  429. '\n<main>' +
  430. '\n\tfoobar' +
  431. '\n</main>'
  432. );
  433. modelDoc.log( 2 );
  434. expectLog(
  435. '<$graveyard>' +
  436. '\n\too' +
  437. '\n</$graveyard>' +
  438. '\n<main>' +
  439. '\n\tfbar' +
  440. '\n</main>'
  441. );
  442. modelDoc.log();
  443. expectLog(
  444. '<$graveyard>' +
  445. '\n\too' +
  446. '\n</$graveyard>' +
  447. '\n<main>' +
  448. '\n\tfbar' +
  449. '\n</main>'
  450. );
  451. viewDoc.log( 0 );
  452. expectLog(
  453. '<$root></$root>'
  454. );
  455. viewDoc.log( 2 );
  456. expectLog(
  457. '<$root>' +
  458. '\n\tfbar' +
  459. '\n</$root>'
  460. );
  461. sinon.spy( modelDoc, 'log' );
  462. sinon.spy( viewDoc, 'log' );
  463. editor.logModel( 1 );
  464. expect( modelDoc.log.calledWithExactly( 1 ), 1 ).to.be.true;
  465. editor.logView( 2 );
  466. expect( viewDoc.log.calledWithExactly( 2 ), 2 ).to.be.true;
  467. modelDoc.log.resetHistory();
  468. viewDoc.log.resetHistory();
  469. editor.logModel();
  470. expect( modelDoc.log.calledWithExactly( 2 ), 3 ).to.be.true;
  471. modelDoc.log.resetHistory();
  472. viewDoc.log.resetHistory();
  473. editor.logDocuments();
  474. expect( modelDoc.log.calledWithExactly( 2 ), 4 ).to.be.true;
  475. expect( viewDoc.log.calledWithExactly( 2 ), 5 ).to.be.true;
  476. modelDoc.log.resetHistory();
  477. viewDoc.log.resetHistory();
  478. editor.logDocuments( 1 );
  479. expect( modelDoc.log.calledWithExactly( 1 ), 6 ).to.be.true;
  480. expect( viewDoc.log.calledWithExactly( 1 ), 7 ).to.be.true;
  481. } );
  482. it( 'should remove old states', () => {
  483. const model = editor.model;
  484. const modelDoc = model.document;
  485. const modelRoot = model.document.getRoot();
  486. for ( let i = 0; i < 25; i++ ) {
  487. const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), new ModelText( 'foobar' ), modelDoc.version );
  488. model.applyOperation( insert );
  489. }
  490. modelDoc.log( 0 );
  491. expectLog( 'Tree log unavailable for given version: 0' );
  492. } );
  493. } );
  494. describe( 'should provide methods for operation replayer', () => {
  495. it( 'getAppliedOperations()', () => {
  496. const model = new Model();
  497. const modelDoc = model.document;
  498. expect( model.getAppliedOperations() ).to.equal( '' );
  499. const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
  500. const element = new ModelElement( 'paragraph' );
  501. otherRoot._appendChild( element );
  502. const insert = new InsertOperation( ModelPosition.createAt( element, 0 ), new ModelText( 'foo' ), 0 );
  503. model.applyOperation( insert );
  504. const stringifiedOperations = model.getAppliedOperations();
  505. expect( stringifiedOperations ).to.equal( JSON.stringify( insert.toJSON() ) );
  506. } );
  507. it( 'createReplayer()', () => {
  508. const model = new Model();
  509. const modelDoc = model.document;
  510. const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
  511. const element = new ModelElement( 'paragraph' );
  512. otherRoot._appendChild( element );
  513. const insert = new InsertOperation( ModelPosition.createAt( element, 0 ), new ModelText( 'foo' ), 0 );
  514. model.applyOperation( insert );
  515. const stringifiedOperations = model.getAppliedOperations();
  516. const operationReplayer = model.createReplayer( stringifiedOperations );
  517. expect( operationReplayer.getOperationsToReplay() ).to.deep.equal( [ JSON.parse( stringifiedOperations ) ] );
  518. } );
  519. } );
  520. describe( 'should provide error logging for transformation', () => {
  521. let model, document, root, otherRoot;
  522. beforeEach( () => {
  523. model = new Model();
  524. document = model.document;
  525. root = document.createRoot();
  526. otherRoot = document.createRoot( 'other', 'other' );
  527. } );
  528. it.skip( 'with more important operation A', () => {
  529. const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
  530. const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
  531. expect( () => {
  532. transform.transform( opA, opB, { isStrong: true } );
  533. } ).to.throw( Error );
  534. expect( error.calledWith( opA.toString() + ' (important)' ) ).to.be.true;
  535. expect( error.calledWith( opB.toString() ) ).to.be.true;
  536. } );
  537. it.skip( 'with more important operation B', () => {
  538. const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
  539. const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
  540. testUtils.sinon.stub( transform, 'transform' ).throws( new Error() );
  541. expect( () => transform.transform( opA, opB, { isStrong: true } ) ).to.throw( Error );
  542. expect( error.calledWith( opA.toString() ) ).to.be.true;
  543. expect( error.calledWith( opB.toString() + ' (important)' ) ).to.be.true;
  544. } );
  545. } );
  546. function expectLog( expectedLogMsg ) {
  547. expect( log.calledWithExactly( expectedLogMsg ) ).to.be.true;
  548. log.resetHistory();
  549. }
  550. } );