| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183 |
- /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
- import { default as enableEngineDebug, disableEngineDebug } from '../../src/dev-utils/enableenginedebug';
- import Editor from '@ckeditor/ckeditor5-core/src/editor/editor';
- import StandardEditor from '@ckeditor/ckeditor5-core/src/editor/standardeditor';
- import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
- import ModelPosition from '../../src/model/position';
- import ModelRange from '../../src/model/range';
- import ModelText from '../../src/model/text';
- import ModelTextProxy from '../../src/model/textproxy';
- import ModelElement from '../../src/model/element';
- import AttributeOperation from '../../src/model/operation/attributeoperation';
- import DetachOperation from '../../src/model/operation/detachoperation';
- import InsertOperation from '../../src/model/operation/insertoperation';
- import MarkerOperation from '../../src/model/operation/markeroperation';
- import MoveOperation from '../../src/model/operation/moveoperation';
- import NoOperation from '../../src/model/operation/nooperation';
- import RenameOperation from '../../src/model/operation/renameoperation';
- import RootAttributeOperation from '../../src/model/operation/rootattributeoperation';
- import RemoveOperation from '../../src/model/operation/removeoperation';
- import DeltaFactory from '../../src/model/delta/deltafactory';
- import Delta from '../../src/model/delta/delta';
- import AttributeDelta from '../../src/model/delta/attributedelta';
- import InsertDelta from '../../src/model/delta/insertdelta';
- import MarkerDelta from '../../src/model/delta/markerdelta';
- import MergeDelta from '../../src/model/delta/mergedelta';
- import MoveDelta from '../../src/model/delta/movedelta';
- import RenameDelta from '../../src/model/delta/renamedelta';
- import RootAttributeDelta from '../../src/model/delta/rootattributedelta';
- import SplitDelta from '../../src/model/delta/splitdelta';
- import UnwrapDelta from '../../src/model/delta/unwrapdelta';
- import WrapDelta from '../../src/model/delta/wrapdelta';
- import deltaTransform from '../../src/model/delta/transform';
- import Model from '../../src/model/model';
- import ModelDocumentFragment from '../../src/model/documentfragment';
- import ViewDocument from '../../src/view/document';
- import ViewAttributeElement from '../../src/view/attributeelement';
- import ViewContainerElement from '../../src/view/containerelement';
- import ViewText from '../../src/view/text';
- import ViewTextProxy from '../../src/view/textproxy';
- import ViewDocumentFragment from '../../src/view/documentfragment';
- import ViewElement from '../../src/view/element';
- import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
- testUtils.createSinonSandbox();
- /* global document */
- describe( 'enableEngineDebug', () => {
- afterEach( () => {
- disableEngineDebug();
- } );
- it( 'should return plugin class', () => {
- const result = enableEngineDebug();
- expect( result.prototype ).to.be.instanceof( Plugin );
- } );
- it( 'should not throw when called multiple times', () => {
- enableEngineDebug();
- enableEngineDebug();
- const result = enableEngineDebug();
- expect( result.prototype ).to.be.instanceof( Plugin );
- } );
- } );
- describe( 'disableEngineDebug', () => {
- it( 'restores modified stubs', () => {
- expect( ModelPosition.prototype.log ).to.equal( undefined, 'Initial value (model/position)' );
- expect( ModelElement.prototype.printTree ).to.equal( undefined, 'Initial value (model/element)' );
- expect( Delta.prototype.log ).to.equal( undefined, 'Initial value (model/delta/delta)' );
- expect( ViewElement.prototype.printTree ).to.equal( undefined, 'Initial value (view/element)' );
- expect( Model.prototype.createReplayer ).to.equal( undefined, 'Initial value (model/document)' );
- expect( Editor.prototype.logDocuments ).to.equal( undefined, 'Initial value (core~editor/editor)' );
- enableEngineDebug();
- expect( ModelPosition.prototype.log ).to.be.a( 'function', 'After enabling engine debug (model/position)' );
- expect( ModelElement.prototype.printTree ).to.be.a( 'function', 'After enabling engine debug (model/element)' );
- expect( Delta.prototype.log ).to.be.a( 'function', 'After enabling engine debug (model/delta/delta)' );
- expect( ViewElement.prototype.printTree ).to.be.a( 'function', 'After enabling engine debug (view/element)' );
- expect( Model.prototype.createReplayer ).to.be.a( 'function', 'After enabling engine debug (model/document)' );
- expect( Editor.prototype.logDocuments ).to.be.a( 'function', 'After enabling engine debug (core~editor/editor)' );
- disableEngineDebug();
- expect( ModelPosition.prototype.log ).to.equal( undefined, 'After disabling engine debug (model/position)' );
- expect( ModelElement.prototype.printTree ).to.equal( undefined, 'After disabling engine debug (model/element)' );
- expect( Delta.prototype.log ).to.equal( undefined, 'After disabling engine debug (model/delta/delta)' );
- expect( ViewElement.prototype.printTree ).to.equal( undefined, 'After disabling engine debug (view/element)' );
- expect( Model.prototype.createReplayer ).to.equal( undefined, 'After disabling engine debug (model/document)' );
- expect( Editor.prototype.logDocuments ).to.equal( undefined, 'After disabling engine debug (core~editor/editor)' );
- } );
- } );
- describe( 'debug tools', () => {
- let DebugPlugin, log, error;
- class TestEditor extends StandardEditor {
- constructor( ...args ) {
- super( ...args );
- this.model.document.createRoot( 'main' );
- this.editing.createRoot( this.element, 'main' );
- }
- }
- before( () => {
- log = sinon.spy();
- error = sinon.spy();
- DebugPlugin = enableEngineDebug( { log, error } );
- } );
- after( () => {
- disableEngineDebug();
- } );
- afterEach( () => {
- log.reset();
- } );
- describe( 'should provide logging tools', () => {
- let model, modelDoc, modelRoot, modelElement, modelDocFrag;
- beforeEach( () => {
- model = new Model();
- modelDoc = model.document;
- modelRoot = modelDoc.createRoot();
- modelElement = new ModelElement( 'paragraph', null, new ModelText( 'foo' ) );
- modelDocFrag = new ModelDocumentFragment( [ new ModelText( 'bar' ) ] );
- } );
- it( 'for ModelText', () => {
- const foo = new ModelText( 'foo', { foo: 'bar' } );
- expect( foo.toString() ).to.equal( '#foo' );
- foo.log();
- expect( log.calledWithExactly( 'ModelText: #foo' ) ).to.be.true;
- foo.logExtended();
- expect( log.calledWithExactly( 'ModelText: #foo, attrs: {"foo":"bar"}' ) ).to.be.true;
- } );
- it( 'for ModelTextProxy', () => {
- const foo = new ModelText( 'foo', { foo: 'bar' } );
- const proxy = new ModelTextProxy( foo, 1, 1 );
- expect( proxy.toString() ).to.equal( '#o' );
- proxy.log();
- expect( log.calledWithExactly( 'ModelTextProxy: #o' ) ).to.be.true;
- proxy.logExtended();
- expect( log.calledWithExactly( 'ModelTextProxy: #o, attrs: {"foo":"bar"}' ) ).to.be.true;
- } );
- it( 'for ModelElement', () => {
- const paragraph = new ModelElement( 'paragraph', { foo: 'bar' }, new ModelText( 'foo' ) );
- expect( paragraph.toString() ).to.equal( '<paragraph>' );
- paragraph.log();
- expectLog( 'ModelElement: <paragraph>' );
- paragraph.logExtended();
- expectLog( 'ModelElement: <paragraph>, 1 children, attrs: {"foo":"bar"}' );
- sinon.spy( paragraph, 'logExtended' );
- paragraph.logAll();
- expect( paragraph.logExtended.called ).to.be.true;
- expectLog( 'ModelText: #foo' );
- } );
- it( 'for ModelRootElement', () => {
- modelRoot.log();
- expectLog( 'ModelRootElement: main' );
- } );
- it( 'for ModelDocumentFragment', () => {
- modelDocFrag.log();
- expectLog( 'ModelDocumentFragment: documentFragment' );
- } );
- it( 'for ModelPosition', () => {
- const posInRoot = new ModelPosition( modelRoot, [ 0, 1, 0 ] );
- const posInElement = new ModelPosition( modelElement, [ 0 ] );
- const posInDocFrag = new ModelPosition( modelDocFrag, [ 2, 3 ] );
- expect( posInRoot.toString() ).to.equal( 'main [ 0, 1, 0 ]' );
- expect( posInElement.toString() ).to.equal( '<paragraph> [ 0 ]' );
- expect( posInDocFrag.toString() ).to.equal( 'documentFragment [ 2, 3 ]' );
- posInRoot.log();
- expectLog( 'ModelPosition: main [ 0, 1, 0 ]' );
- } );
- it( 'for ModelRange', () => {
- const rangeInRoot = ModelRange.createIn( modelRoot );
- const rangeInElement = ModelRange.createIn( modelElement );
- const rangeInDocFrag = ModelRange.createIn( modelDocFrag );
- expect( rangeInRoot.toString() ).to.equal( 'main [ 0 ] - [ 0 ]' );
- expect( rangeInElement.toString() ).to.equal( '<paragraph> [ 0 ] - [ 3 ]' );
- expect( rangeInDocFrag.toString() ).to.equal( 'documentFragment [ 0 ] - [ 3 ]' );
- rangeInRoot.log();
- expectLog( 'ModelRange: main [ 0 ] - [ 0 ]' );
- } );
- it( 'for ViewText', () => {
- const foo = new ViewText( 'foo' );
- expect( foo.toString() ).to.equal( '#foo' );
- foo.log();
- expect( log.calledWithExactly( 'ViewText: #foo' ) ).to.be.true;
- foo.logExtended();
- expect( log.calledWithExactly( 'ViewText: #foo' ) ).to.be.true;
- } );
- it( 'for ViewTextProxy', () => {
- const foo = new ViewText( 'foo', { foo: 'bar' } );
- const proxy = new ViewTextProxy( foo, 1, 1 );
- expect( proxy.toString() ).to.equal( '#o' );
- proxy.log();
- expect( log.calledWithExactly( 'ViewTextProxy: #o' ) ).to.be.true;
- proxy.logExtended();
- expect( log.calledWithExactly( 'ViewTextProxy: #o' ) ).to.be.true;
- } );
- describe( 'for operations', () => {
- beforeEach( () => {
- modelRoot.appendChildren( [ new ModelText( 'foobar' ) ] );
- } );
- it( 'AttributeOperation', () => {
- const op = new AttributeOperation( ModelRange.createIn( modelRoot ), 'key', null, { foo: 'bar' }, 0 );
- expect( op.toString() ).to.equal( 'AttributeOperation( 0 ): "key": null -> {"foo":"bar"}, main [ 0 ] - [ 6 ]' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'DetachOperation (text node)', () => {
- const op = new DetachOperation( ModelPosition.createAt( modelRoot, 0 ), 3, 0 );
- expect( op.toString() ).to.equal( 'DetachOperation( 0 ): #foo -> main [ 0 ] - [ 3 ]' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'DetachOperation (element)', () => {
- const element = new ModelElement( 'element' );
- modelRoot.insertChildren( 0, element );
- const op = new DetachOperation( ModelPosition.createBefore( element ), 1, 0 );
- expect( op.toString() ).to.equal( 'DetachOperation( 0 ): <element> -> main [ 0 ] - [ 1 ]' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'DetachOperation (multiple nodes)', () => {
- const element = new ModelElement( 'element' );
- modelRoot.insertChildren( 0, element );
- const op = new DetachOperation( ModelPosition.createBefore( element ), 2, 0 );
- expect( op.toString() ).to.equal( 'DetachOperation( 0 ): [ 2 ] -> main [ 0 ] - [ 2 ]' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'InsertOperation (text node)', () => {
- const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelText( 'abc' ) ], 0 );
- expect( op.toString() ).to.equal( 'InsertOperation( 0 ): #abc -> main [ 3 ]' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'InsertOperation (element)', () => {
- const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelElement( 'paragraph' ) ], 0 );
- expect( op.toString() ).to.equal( 'InsertOperation( 0 ): <paragraph> -> main [ 3 ]' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'InsertOperation (multiple nodes)', () => {
- const nodes = [ new ModelText( 'x' ), new ModelElement( 'y' ), new ModelText( 'z' ) ];
- const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), nodes, 0 );
- expect( op.toString() ).to.equal( 'InsertOperation( 0 ): [ 3 ] -> main [ 3 ]' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'MarkerOperation', () => {
- const op = new MarkerOperation( 'marker', null, ModelRange.createIn( modelRoot ), modelDoc.markers, 0 );
- expect( op.toString() ).to.equal( 'MarkerOperation( 0 ): "marker": null -> main [ 0 ] - [ 6 ]' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'MoveOperation', () => {
- const op = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, ModelPosition.createAt( modelRoot, 6 ), 0 );
- expect( op.toString() ).to.equal( 'MoveOperation( 0 ): main [ 1 ] - [ 3 ] -> main [ 6 ]' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'MoveOperation sticky', () => {
- const op = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, ModelPosition.createAt( modelRoot, 6 ), 0 );
- op.isSticky = true;
- expect( op.toString() ).to.equal( 'MoveOperation( 0 ): main [ 1 ] - [ 3 ] -> main [ 6 ] (sticky)' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'NoOperation', () => {
- const op = new NoOperation( 0 );
- expect( op.toString() ).to.equal( 'NoOperation( 0 )' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'RenameOperation', () => {
- const op = new RenameOperation( ModelPosition.createAt( modelRoot, 1 ), 'old', 'new', 0 );
- expect( op.toString() ).to.equal( 'RenameOperation( 0 ): main [ 1 ]: "old" -> "new"' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- it( 'RootAttributeOperation', () => {
- const op = new RootAttributeOperation( modelRoot, 'key', 'old', null, 0 );
- expect( op.toString() ).to.equal( 'RootAttributeOperation( 0 ): "key": "old" -> null, main' );
- op.log();
- expect( log.calledWithExactly( op.toString() ) ).to.be.true;
- } );
- } );
- describe( 'for deltas', () => {
- it( 'Delta', () => {
- const delta = new Delta();
- const op = { log: sinon.spy() };
- delta.addOperation( op );
- sinon.spy( delta, 'log' );
- delta.logAll();
- expect( op.log.called ).to.be.true;
- expect( delta.log.called ).to.be.true;
- } );
- it( 'AttributeDelta', () => {
- modelRoot.appendChildren( new ModelText( 'foobar' ) );
- const delta = new AttributeDelta();
- const op = new AttributeOperation( ModelRange.createIn( modelRoot ), 'key', null, { foo: 'bar' }, 0 );
- delta.addOperation( op );
- expect( delta.toString() ).to.equal( 'AttributeDelta( 0 ): "key": -> {"foo":"bar"}, main [ 0 ] - [ 6 ], 1 ops' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'InsertDelta (text node)', () => {
- const delta = new InsertDelta();
- const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelText( 'abc' ) ], 0 );
- delta.addOperation( op );
- expect( delta.toString() ).to.equal( 'InsertDelta( 0 ): #abc -> main [ 3 ]' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'InsertDelta (element)', () => {
- const delta = new InsertDelta();
- const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelElement( 'paragraph' ) ], 0 );
- delta.addOperation( op );
- expect( delta.toString() ).to.equal( 'InsertDelta( 0 ): <paragraph> -> main [ 3 ]' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'InsertDelta (multiple nodes)', () => {
- const delta = new InsertDelta();
- const nodes = [ new ModelText( 'x' ), new ModelElement( 'y' ), new ModelText( 'z' ) ];
- const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), nodes, 0 );
- delta.addOperation( op );
- expect( delta.toString() ).to.equal( 'InsertDelta( 0 ): [ 3 ] -> main [ 3 ]' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'MarkerDelta', () => {
- modelRoot.appendChildren( new ModelText( 'foobar' ) );
- const delta = new MarkerDelta();
- const op = new MarkerOperation( 'marker', null, ModelRange.createIn( modelRoot ), modelDoc.markers, 0 );
- delta.addOperation( op );
- expect( delta.toString() ).to.equal( 'MarkerDelta( 0 ): "marker": null -> main [ 0 ] - [ 6 ]' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'MergeDelta', () => {
- const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
- const firstEle = new ModelElement( 'paragraph' );
- const removedEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
- otherRoot.appendChildren( [ firstEle, removedEle ] );
- const graveyard = modelDoc.graveyard;
- const delta = new MergeDelta();
- const move = new MoveOperation( ModelPosition.createAt( removedEle, 0 ), 3, ModelPosition.createAt( firstEle, 0 ), 0 );
- const remove = new RemoveOperation(
- ModelPosition.createBefore( removedEle ),
- 1,
- ModelPosition.createAt( graveyard, 0 ),
- 1
- );
- delta.addOperation( move );
- delta.addOperation( remove );
- expect( delta.toString() ).to.equal( 'MergeDelta( 0 ): otherRoot [ 1 ]' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'MergeDelta - NoOperation as second operation', () => {
- const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
- const firstEle = new ModelElement( 'paragraph' );
- const removedEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
- otherRoot.appendChildren( [ firstEle, removedEle ] );
- const delta = new MergeDelta();
- const move = new MoveOperation( ModelPosition.createAt( removedEle, 0 ), 3, ModelPosition.createAt( firstEle, 0 ), 0 );
- delta.addOperation( move );
- delta.addOperation( new NoOperation( 1 ) );
- expect( delta.toString() ).to.equal( 'MergeDelta( 0 ): (move from otherRoot [ 1, 0 ])' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'MoveDelta', () => {
- const delta = new MoveDelta();
- const move1 = new MoveOperation( ModelPosition.createAt( modelRoot, 0 ), 1, ModelPosition.createAt( modelRoot, 3 ), 0 );
- const move2 = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 1, ModelPosition.createAt( modelRoot, 6 ), 0 );
- delta.addOperation( move1 );
- delta.addOperation( move2 );
- expect( delta.toString() ).to.equal( 'MoveDelta( 0 ): main [ 0 ] - [ 1 ] -> main [ 3 ]; main [ 1 ] - [ 2 ] -> main [ 6 ]' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'RenameDelta', () => {
- const delta = new RenameDelta();
- const op = new RenameOperation( ModelPosition.createAt( modelRoot, 1 ), 'old', 'new', 0 );
- delta.addOperation( op );
- expect( delta.toString() ).to.equal( 'RenameDelta( 0 ): main [ 1 ]: "old" -> "new"' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'RootAttributeDelta', () => {
- const delta = new RootAttributeDelta();
- const op = new RootAttributeOperation( modelRoot, 'key', 'old', null, 0 );
- delta.addOperation( op );
- expect( delta.toString() ).to.equal( 'RootAttributeDelta( 0 ): "key": "old" -> null, main' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'SplitDelta', () => {
- const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
- const splitEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
- otherRoot.appendChildren( [ splitEle ] );
- const delta = new SplitDelta();
- const insert = new InsertOperation( ModelPosition.createAt( otherRoot, 1 ), [ new ModelElement( 'paragraph' ) ], 0 );
- const move = new MoveOperation( ModelPosition.createAt( splitEle, 1 ), 2, new ModelPosition( otherRoot, [ 1, 0 ] ), 1 );
- delta.addOperation( insert );
- delta.addOperation( move );
- expect( delta.toString() ).to.equal( 'SplitDelta( 0 ): otherRoot [ 0, 1 ]' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'SplitDelta - NoOperation as second operation', () => {
- const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
- const splitEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
- otherRoot.appendChildren( [ splitEle ] );
- const delta = new SplitDelta();
- const insert = new InsertOperation( ModelPosition.createAt( otherRoot, 1 ), [ new ModelElement( 'paragraph' ) ], 0 );
- const move = new NoOperation( 1 );
- delta.addOperation( insert );
- delta.addOperation( move );
- expect( delta.toString() ).to.equal( 'SplitDelta( 0 ): (clone to otherRoot [ 1 ])' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'SplitDelta - NoOperation as second operation, MoveOperation as first operation', () => {
- const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
- const delta = new SplitDelta();
- const insert = new MoveOperation( ModelPosition.createAt( modelRoot, 1 ), 1, ModelPosition.createAt( otherRoot, 1 ), 0 );
- const move = new NoOperation( 1 );
- delta.addOperation( insert );
- delta.addOperation( move );
- expect( delta.toString() ).to.equal( 'SplitDelta( 0 ): (clone to otherRoot [ 1 ])' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'UnwrapDelta', () => {
- const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
- const unwrapEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
- otherRoot.appendChildren( [ unwrapEle ] );
- const graveyard = modelDoc.graveyard;
- const delta = new UnwrapDelta();
- const move = new MoveOperation( ModelPosition.createAt( unwrapEle, 0 ), 3, ModelPosition.createAt( otherRoot, 0 ), 0 );
- const remove = new RemoveOperation( ModelPosition.createAt( otherRoot, 3 ), 1, ModelPosition.createAt( graveyard, 0 ), 1 );
- delta.addOperation( move );
- delta.addOperation( remove );
- expect( delta.toString() ).to.equal( 'UnwrapDelta( 0 ): otherRoot [ 0 ]' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- it( 'WrapDelta', () => {
- const delta = new WrapDelta();
- const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 6 ), new ModelElement( 'paragraph' ), 0 );
- const move = new MoveOperation( ModelPosition.createAt( modelRoot, 0 ), 6, new ModelPosition( modelRoot, [ 1, 0 ] ), 1 );
- delta.addOperation( insert );
- delta.addOperation( move );
- expect( delta.toString() ).to.equal( 'WrapDelta( 0 ): main [ 0 ] - [ 6 ] -> <paragraph>' );
- delta.log();
- expect( log.calledWithExactly( delta.toString() ) ).to.be.true;
- } );
- } );
- it( 'for applied operations', () => {
- const delta = new InsertDelta();
- const op = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), [ new ModelText( 'foo' ) ], 0 );
- delta.addOperation( op );
- model.applyOperation( op );
- expect( log.calledWithExactly( 'Applying InsertOperation( 0 ): #foo -> main [ 0 ]' ) ).to.be.true;
- } );
- } );
- describe( 'should provide tree printing tools', () => {
- it( 'for model', () => {
- const model = new Model();
- const modelDoc = model.document;
- const modelRoot = modelDoc.createRoot();
- modelRoot.appendChildren( [
- new ModelElement( 'paragraph', { foo: 'bar' }, [
- new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' )
- ] ),
- new ModelElement( 'listItem', { type: 'numbered', indent: 0 }, new ModelText( 'One.' ) ),
- ] );
- const modelRootTree = modelRoot.printTree();
- expect( modelRootTree ).to.equal(
- '<main>' +
- '\n\t<paragraph foo="bar">' +
- '\n\t\tThis is ' +
- '\n\t\t<$text bold=true>bold</$text>' +
- '\n\t\t.' +
- '\n\t</paragraph>' +
- '\n\t<listItem type="numbered" indent=0>' +
- '\n\t\tOne.' +
- '\n\t</listItem>' +
- '\n</main>'
- );
- modelRoot.logTree();
- expect( log.calledWithExactly( modelRootTree ) ).to.be.true;
- const modelParagraph = modelRoot.getChild( 0 );
- const modelParagraphTree = modelParagraph.printTree();
- expect( modelParagraphTree ).to.equal(
- '<paragraph foo="bar">' +
- '\n\tThis is ' +
- '\n\t<$text bold=true>bold</$text>' +
- '\n\t.' +
- '\n</paragraph>'
- );
- log.reset();
- modelParagraph.logTree();
- expect( log.calledWithExactly( modelParagraphTree ) ).to.be.true;
- const modelDocFrag = new ModelDocumentFragment( [
- new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' ),
- new ModelElement( 'paragraph', { foo: 'bar' }, [
- new ModelText( 'This is ' ), new ModelText( 'bold', { bold: true } ), new ModelText( '.' )
- ] )
- ] );
- const modelDocFragTree = modelDocFrag.printTree();
- expect( modelDocFragTree ).to.equal(
- 'ModelDocumentFragment: [' +
- '\n\tThis is ' +
- '\n\t<$text bold=true>bold</$text>' +
- '\n\t.' +
- '\n\t<paragraph foo="bar">' +
- '\n\t\tThis is ' +
- '\n\t\t<$text bold=true>bold</$text>' +
- '\n\t\t.' +
- '\n\t</paragraph>' +
- '\n]'
- );
- log.reset();
- modelDocFrag.logTree();
- expect( log.calledWithExactly( modelDocFragTree ) ).to.be.true;
- } );
- it( 'for view', () => {
- const viewDoc = new ViewDocument();
- const viewRoot = viewDoc.createRoot( 'div' );
- viewRoot.appendChildren( [
- new ViewContainerElement( 'p', { foo: 'bar' }, [
- new ViewText( 'This is ' ), new ViewAttributeElement( 'b', null, new ViewText( 'bold' ) ), new ViewText( '.' )
- ] ),
- new ViewContainerElement( 'ol', null, [
- new ViewContainerElement( 'li', null, new ViewText( 'One.' ) )
- ] )
- ] );
- const viewRootTree = viewRoot.printTree();
- expect( viewRootTree ).to.equal(
- '<div>' +
- '\n\t<p foo="bar">' +
- '\n\t\tThis is ' +
- '\n\t\t<b>' +
- '\n\t\t\tbold' +
- '\n\t\t</b>' +
- '\n\t\t.' +
- '\n\t</p>' +
- '\n\t<ol>' +
- '\n\t\t<li>' +
- '\n\t\t\tOne.' +
- '\n\t\t</li>' +
- '\n\t</ol>' +
- '\n</div>'
- );
- viewRoot.logTree();
- expect( log.calledWithExactly( viewRootTree ) ).to.be.true;
- const viewParagraph = viewRoot.getChild( 0 );
- const viewParagraphTree = viewParagraph.printTree();
- expect( viewParagraphTree ).to.equal(
- '<p foo="bar">' +
- '\n\tThis is ' +
- '\n\t<b>' +
- '\n\t\tbold' +
- '\n\t</b>' +
- '\n\t.' +
- '\n</p>'
- );
- log.reset();
- viewParagraph.logTree();
- expect( log.calledWithExactly( viewParagraphTree ) ).to.be.true;
- const viewDocFrag = new ViewDocumentFragment( [
- new ViewText( 'Text.' ),
- new ViewContainerElement( 'p', { foo: 'bar' }, [
- new ViewText( 'This is ' ), new ViewAttributeElement( 'b', null, new ViewText( 'bold' ) ), new ViewText( '.' )
- ] )
- ] );
- const viewDocFragTree = viewDocFrag.printTree();
- expect( viewDocFragTree ).to.equal(
- 'ViewDocumentFragment: [' +
- '\n\tText.' +
- '\n\t<p foo="bar">' +
- '\n\t\tThis is ' +
- '\n\t\t<b>' +
- '\n\t\t\tbold' +
- '\n\t\t</b>' +
- '\n\t\t.' +
- '\n\t</p>' +
- '\n]'
- );
- log.reset();
- viewDocFrag.logTree();
- expect( log.calledWithExactly( viewDocFragTree ) ).to.be.true;
- } );
- } );
- describe( 'should store model and view trees state', () => {
- let editor;
- beforeEach( () => {
- const div = document.createElement( 'div' );
- return TestEditor.create( div, {
- plugins: [ DebugPlugin ]
- } ).then( _editor => {
- editor = _editor;
- } );
- } );
- it( 'should store model and view state after each applied operation', () => {
- const model = editor.model;
- const modelDoc = model.document;
- const modelRoot = modelDoc.getRoot();
- const viewDoc = editor.editing.view;
- model.change( () => {
- const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), new ModelText( 'foobar' ), 0 );
- model.applyOperation( wrapInDelta( insert ) );
- const graveyard = modelDoc.graveyard;
- const remove = new RemoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, ModelPosition.createAt( graveyard, 0 ), 1 );
- model.applyOperation( wrapInDelta( remove ) );
- } );
- log.reset();
- modelDoc.log( 0 );
- expectLog(
- '<$graveyard></$graveyard>' +
- '\n<main></main>'
- );
- modelDoc.log( 1 );
- expectLog(
- '<$graveyard></$graveyard>' +
- '\n<main>' +
- '\n\tfoobar' +
- '\n</main>'
- );
- modelDoc.log( 2 );
- expectLog(
- '<$graveyard>' +
- '\n\too' +
- '\n</$graveyard>' +
- '\n<main>' +
- '\n\tfbar' +
- '\n</main>'
- );
- modelDoc.log();
- expectLog(
- '<$graveyard>' +
- '\n\too' +
- '\n</$graveyard>' +
- '\n<main>' +
- '\n\tfbar' +
- '\n</main>'
- );
- viewDoc.log( 0 );
- expectLog(
- '<div></div>'
- );
- viewDoc.log( 2 );
- expectLog(
- '<div>' +
- '\n\tfbar' +
- '\n</div>'
- );
- sinon.spy( modelDoc, 'log' );
- sinon.spy( viewDoc, 'log' );
- editor.logModel( 1 );
- expect( modelDoc.log.calledWithExactly( 1 ), 1 ).to.be.true;
- editor.logView( 2 );
- expect( viewDoc.log.calledWithExactly( 2 ), 2 ).to.be.true;
- modelDoc.log.reset();
- viewDoc.log.reset();
- editor.logModel();
- expect( modelDoc.log.calledWithExactly( 2 ), 3 ).to.be.true;
- modelDoc.log.reset();
- viewDoc.log.reset();
- editor.logDocuments();
- expect( modelDoc.log.calledWithExactly( 2 ), 4 ).to.be.true;
- expect( viewDoc.log.calledWithExactly( 2 ), 5 ).to.be.true;
- modelDoc.log.reset();
- viewDoc.log.reset();
- editor.logDocuments( 1 );
- expect( modelDoc.log.calledWithExactly( 1 ), 6 ).to.be.true;
- expect( viewDoc.log.calledWithExactly( 1 ), 7 ).to.be.true;
- } );
- it( 'should remove old states', () => {
- const model = editor.model;
- const modelDoc = model.document;
- const modelRoot = model.document.getRoot();
- for ( let i = 0; i < 25; i++ ) {
- const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), new ModelText( 'foobar' ), modelDoc.version );
- model.applyOperation( wrapInDelta( insert ) );
- }
- modelDoc.log( 0 );
- expectLog( 'Tree log unavailable for given version: 0' );
- } );
- } );
- describe( 'should provide methods for delta replayer', () => {
- it( 'getAppliedDeltas()', () => {
- const model = new Model();
- const modelDoc = model.document;
- expect( model.getAppliedDeltas() ).to.equal( '' );
- const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
- const firstEle = new ModelElement( 'paragraph' );
- const removedEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
- otherRoot.appendChildren( [ firstEle, removedEle ] );
- const delta = new MergeDelta();
- const graveyard = modelDoc.graveyard;
- const move = new MoveOperation( ModelPosition.createAt( removedEle, 0 ), 3, ModelPosition.createAt( firstEle, 0 ), 0 );
- const remove = new RemoveOperation( ModelPosition.createBefore( removedEle ), 1, ModelPosition.createAt( graveyard, 0 ), 1 );
- delta.addOperation( move );
- delta.addOperation( remove );
- model.applyOperation( move );
- model.applyOperation( remove );
- const stringifiedDeltas = model.getAppliedDeltas();
- expect( stringifiedDeltas ).to.equal( JSON.stringify( delta.toJSON() ) );
- } );
- it( 'createReplayer()', () => {
- const model = new Model();
- const modelDoc = model.document;
- const otherRoot = modelDoc.createRoot( '$root', 'otherRoot' );
- const firstEle = new ModelElement( 'paragraph' );
- const removedEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
- otherRoot.appendChildren( [ firstEle, removedEle ] );
- const delta = new MergeDelta();
- const graveyard = modelDoc.graveyard;
- const move = new MoveOperation( ModelPosition.createAt( removedEle, 0 ), 3, ModelPosition.createAt( firstEle, 0 ), 0 );
- const remove = new RemoveOperation( ModelPosition.createBefore( removedEle ), 1, ModelPosition.createAt( graveyard, 0 ), 1 );
- delta.addOperation( move );
- delta.addOperation( remove );
- const stringifiedDeltas = JSON.stringify( delta.toJSON() );
- const deltaReplayer = model.createReplayer( stringifiedDeltas );
- expect( deltaReplayer.getDeltasToReplay() ).to.deep.equal( [ JSON.parse( stringifiedDeltas ) ] );
- } );
- } );
- describe( 'should provide debug tools for delta transformation', () => {
- let model, document, root, otherRoot;
- beforeEach( () => {
- model = new Model();
- document = model.document;
- root = document.createRoot();
- otherRoot = document.createRoot( 'other', 'other' );
- } );
- it( 'Delta#_saveHistory()', () => {
- const insertDeltaA = new InsertDelta();
- const insertOpA = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
- insertDeltaA.addOperation( insertOpA );
- const insertDeltaB = new InsertDelta();
- const insertOpB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
- insertDeltaB.addOperation( insertOpB );
- const insertDeltaFinalA = new InsertDelta();
- const insertOpFinalA = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 1 );
- insertDeltaFinalA.addOperation( insertOpFinalA );
- const insertDeltaFinalAJsonWithoutHistory = JSON.stringify( insertDeltaFinalA );
- insertDeltaFinalA._saveHistory( {
- before: insertDeltaA,
- transformedBy: insertDeltaB,
- wasImportant: true,
- resultIndex: 0,
- resultsTotal: 1
- } );
- const insertDeltaC = new InsertDelta();
- const insertOpC = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 1 );
- insertDeltaC.addOperation( insertOpC );
- const insertDeltaFinalB = new InsertDelta();
- const insertOpFinalB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 2 );
- insertDeltaFinalB.addOperation( insertOpFinalB );
- insertDeltaFinalB._saveHistory( {
- before: insertDeltaFinalA,
- transformedBy: insertDeltaC,
- wasImportant: false,
- resultIndex: 1,
- resultsTotal: 3
- } );
- expect( insertDeltaA.history ).to.be.undefined;
- expect( insertDeltaB.history ).to.be.undefined;
- expect( insertDeltaFinalA.history ).not.to.be.undefined;
- expect( insertDeltaFinalA.history.length ).to.equal( 1 );
- expect( insertDeltaFinalA.history[ 0 ] ).to.deep.equal( {
- before: JSON.stringify( insertDeltaA ),
- transformedBy: JSON.stringify( insertDeltaB ),
- wasImportant: true,
- resultIndex: 0,
- resultsTotal: 1
- } );
- expect( insertDeltaFinalB.history ).not.to.be.undefined;
- expect( insertDeltaFinalB.history.length ).to.equal( 2 );
- expect( insertDeltaFinalB.history[ 0 ] ).to.deep.equal( {
- before: JSON.stringify( insertDeltaA ),
- transformedBy: JSON.stringify( insertDeltaB ),
- wasImportant: true,
- resultIndex: 0,
- resultsTotal: 1
- } );
- expect( insertDeltaFinalB.history[ 1 ] ).to.deep.equal( {
- before: insertDeltaFinalAJsonWithoutHistory,
- transformedBy: JSON.stringify( insertDeltaC ),
- wasImportant: false,
- resultIndex: 1,
- resultsTotal: 3
- } );
- } );
- it( 'save delta history on deltaTransform.transform', () => {
- const deltaA = new MoveDelta();
- const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
- deltaA.addOperation( opA );
- const deltaB = new InsertDelta();
- const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
- deltaB.addOperation( opB );
- const deltaC = new MoveDelta();
- const opC = new MoveOperation( ModelPosition.createAt( root, 4 ), 2, ModelPosition.createAt( root, 0 ), 1 );
- deltaC.addOperation( opC );
- let result = deltaTransform.transform( deltaA, deltaB, { document, wasAffected: new Map() } );
- expect( result[ 0 ].history ).not.to.be.undefined;
- expect( result[ 0 ].history.length ).to.equal( 1 );
- expect( result[ 0 ].history[ 0 ] ).to.deep.equal( {
- before: JSON.stringify( deltaA ),
- transformedBy: JSON.stringify( deltaB ),
- wasImportant: false,
- resultIndex: 0,
- resultsTotal: 1
- } );
- const firstResultWithoutHistory = result[ 0 ].clone();
- delete firstResultWithoutHistory.history;
- result = deltaTransform.transform( result[ 0 ], deltaC, {
- isStrong: true,
- document,
- wasAffected: new Map()
- } );
- expect( result[ 0 ].history ).not.to.be.undefined;
- expect( result[ 0 ].history.length ).to.equal( 2 );
- expect( result[ 0 ].history[ 0 ] ).to.deep.equal( {
- before: JSON.stringify( deltaA ),
- transformedBy: JSON.stringify( deltaB ),
- wasImportant: false,
- resultIndex: 0,
- resultsTotal: 1
- } );
- expect( result[ 0 ].history[ 1 ] ).to.deep.equal( {
- before: JSON.stringify( firstResultWithoutHistory ),
- transformedBy: JSON.stringify( deltaC ),
- wasImportant: true,
- resultIndex: 0,
- resultsTotal: 2
- } );
- } );
- it( 'recreate delta using Delta#history', () => {
- const deltaA = new MoveDelta();
- const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
- deltaA.addOperation( opA );
- const deltaB = new InsertDelta();
- const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
- deltaB.addOperation( opB );
- const deltaC = new MoveDelta();
- const opC = new MoveOperation( ModelPosition.createAt( root, 4 ), 2, ModelPosition.createAt( root, 0 ), 1 );
- deltaC.addOperation( opC );
- let original = deltaTransform.transform( deltaA, deltaB, { document, wasAffected: new Map() } );
- original = deltaTransform.transform( original[ 0 ], deltaC, {
- isStrong: true,
- document,
- wasAffected: new Map()
- } )[ 0 ];
- const history = original.history;
- let recreated = deltaTransform.transform(
- DeltaFactory.fromJSON( JSON.parse( history[ 0 ].before ), document ),
- DeltaFactory.fromJSON( JSON.parse( history[ 0 ].transformedBy ), document ),
- { isStrong: history[ 0 ].wasImportant, document, wasAffected: new Map() }
- );
- recreated = recreated[ history[ 0 ].resultIndex ];
- recreated = deltaTransform.transform(
- recreated,
- DeltaFactory.fromJSON( JSON.parse( history[ 1 ].transformedBy ), document ),
- { isStrong: history[ 1 ].wasImportant, document, wasAffected: new Map() }
- );
- recreated = recreated[ history[ 1 ].resultIndex ];
- delete original.history;
- delete recreated.history;
- expect( JSON.stringify( recreated ) ).to.equal( JSON.stringify( original ) );
- } );
- describe( 'provide additional logging when transformation crashes', () => {
- it( 'with more important delta A', () => {
- const deltaA = new MoveDelta();
- const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
- deltaA.addOperation( opA );
- const deltaB = new InsertDelta();
- const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
- deltaB.addOperation( opB );
- testUtils.sinon.stub( deltaTransform, 'defaultTransform' ).throws( new Error() );
- expect( () => deltaTransform.transform( deltaA, deltaB, { isStrong: true } ) ).to.throw( Error );
- expect( error.calledWith( deltaA.toString() + ' (important)' ) ).to.be.true;
- expect( error.calledWith( deltaB.toString() ) ).to.be.true;
- } );
- it( 'with more important delta B', () => {
- const deltaA = new MoveDelta();
- const opA = new MoveOperation( ModelPosition.createAt( root, 4 ), 4, ModelPosition.createAt( otherRoot, 4 ), 0 );
- deltaA.addOperation( opA );
- const deltaB = new InsertDelta();
- const opB = new InsertOperation( ModelPosition.createAt( root, 0 ), new ModelText( 'a' ), 0 );
- deltaB.addOperation( opB );
- testUtils.sinon.stub( deltaTransform, 'defaultTransform' ).throws( new Error() );
- expect( () => deltaTransform.transform( deltaA, deltaB, { isStrong: false } ) ).to.throw( Error );
- expect( error.calledWith( deltaA.toString() ) ).to.be.true;
- expect( error.calledWith( deltaB.toString() + ' (important)' ) ).to.be.true;
- } );
- } );
- } );
- function expectLog( expectedLogMsg ) {
- expect( log.calledWithExactly( expectedLogMsg ) ).to.be.true;
- log.reset();
- }
- function wrapInDelta( op ) {
- const delta = new Delta();
- delta.addOperation( op );
- return op;
- }
- } );
|