| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985 |
- /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
- import enableEngineDebug from '../../src/dev-utils/enableenginedebug';
- 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 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 { default as AttributeDelta, RootAttributeDelta } 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 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 ModelDocument from '../../src/model/document';
- 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';
- /* global document */
- describe( 'enableEngineDebug', () => {
- 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( 'debug tools', () => {
- let DebugPlugin, log, error;
- class TestEditor extends StandardEditor {
- constructor( ...args ) {
- super( ...args );
- this.document.createRoot( 'main' );
- this.editing.createRoot( this.element, 'main' );
- }
- }
- before( () => {
- log = sinon.spy();
- error = sinon.spy();
- DebugPlugin = enableEngineDebug( { log, error } );
- } );
- afterEach( () => {
- log.reset();
- } );
- describe( 'should provide logging tools', () => {
- let modelDoc, modelRoot, modelElement, modelDocFrag;
- beforeEach( () => {
- modelDoc = new ModelDocument();
- 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( 'InsertOperation', () => {
- const op = new InsertOperation( ModelPosition.createAt( modelRoot, 3 ), [ new ModelText( 'abc' ) ], 0 );
- expect( op.toString() ).to.equal( 'InsertOperation( 0 ): [ 1 ] -> 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( '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', () => {
- 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 ): [ 1 ] -> 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 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, 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( '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( 'UnwrapDelta', () => {
- const otherRoot = modelDoc.createRoot( 'main', 'otherRoot' );
- const unwrapEle = new ModelElement( 'paragraph', null, [ new ModelText( 'foo' ) ] );
- otherRoot.appendChildren( [ unwrapEle ] );
- 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, 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 );
- modelDoc.applyOperation( op );
- expect( log.calledWithExactly( 'Applying InsertOperation( 0 ): [ 1 ] -> main [ 0 ]' ) ).to.be.true;
- } );
- } );
- describe( 'should provide tree printing tools', () => {
- it( 'for model', () => {
- const modelDoc = new ModelDocument();
- 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.document;
- const modelRoot = model.getRoot();
- const view = editor.editing.view;
- const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), new ModelText( 'foobar' ), 0 );
- model.applyOperation( wrapInDelta( insert ) );
- const remove = new RemoveOperation( ModelPosition.createAt( modelRoot, 1 ), 2, 1 );
- model.applyOperation( wrapInDelta( remove ) );
- log.reset();
- model.log( 0 );
- expectLog(
- '<$graveyard></$graveyard>' +
- '\n<main></main>'
- );
- model.log( 1 );
- expectLog(
- '<$graveyard></$graveyard>' +
- '\n<main>' +
- '\n\tfoobar' +
- '\n</main>'
- );
- model.log( 2 );
- expectLog(
- '<$graveyard>' +
- '\n\t<$graveyardHolder>' +
- '\n\t\too' +
- '\n\t</$graveyardHolder>' +
- '\n</$graveyard>' +
- '\n<main>' +
- '\n\tfbar' +
- '\n</main>'
- );
- model.log();
- expectLog(
- '<$graveyard>' +
- '\n\t<$graveyardHolder>' +
- '\n\t\too' +
- '\n\t</$graveyardHolder>' +
- '\n</$graveyard>' +
- '\n<main>' +
- '\n\tfbar' +
- '\n</main>'
- );
- view.log( 0 );
- expectLog(
- '<div></div>'
- );
- view.log( 1 );
- expectLog(
- '<div>' +
- '\n\tfoobar' +
- '\n</div>'
- );
- view.log( 2 );
- expectLog(
- '<div>' +
- '\n\tfbar' +
- '\n</div>'
- );
- sinon.spy( model, 'log' );
- sinon.spy( view, 'log' );
- editor.logModel( 1 );
- expect( model.log.calledWithExactly( 1 ) ).to.be.true;
- editor.logView( 2 );
- expect( view.log.calledWithExactly( 2 ) ).to.be.true;
- model.log.reset();
- view.log.reset();
- editor.logModel();
- expect( model.log.calledWithExactly( 2 ) ).to.be.true;
- model.log.reset();
- view.log.reset();
- editor.logDocuments();
- expect( model.log.calledWithExactly( 2 ) ).to.be.true;
- expect( view.log.calledWithExactly( 2 ) ).to.be.true;
- model.log.reset();
- view.log.reset();
- editor.logDocuments( 1 );
- expect( model.log.calledWithExactly( 1 ) ).to.be.true;
- expect( view.log.calledWithExactly( 1 ) ).to.be.true;
- } );
- it( 'should remove old states', () => {
- const model = editor.document;
- const modelRoot = model.getRoot();
- for ( let i = 0; i < 25; i++ ) {
- const insert = new InsertOperation( ModelPosition.createAt( modelRoot, 0 ), new ModelText( 'foobar' ), model.version );
- model.applyOperation( wrapInDelta( insert ) );
- }
- model.log( 0 );
- expectLog( 'Tree log unavailable for given version: 0' );
- } );
- } );
- describe( 'should provide methods for delta replayer', () => {
- it( 'getAppliedDeltas()', () => {
- const modelDoc = new ModelDocument();
- expect( modelDoc.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 move = new MoveOperation( ModelPosition.createAt( removedEle, 0 ), 3, ModelPosition.createAt( firstEle, 0 ), 0 );
- const remove = new RemoveOperation( ModelPosition.createBefore( removedEle ), 1, 1 );
- delta.addOperation( move );
- delta.addOperation( remove );
- modelDoc.applyOperation( move );
- modelDoc.applyOperation( remove );
- const stringifiedDeltas = modelDoc.getAppliedDeltas();
- expect( stringifiedDeltas ).to.equal( JSON.stringify( delta.toJSON() ) );
- } );
- it( 'createReplayer()', () => {
- const modelDoc = new ModelDocument();
- 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 );
- const remove = new RemoveOperation( ModelPosition.createBefore( removedEle ), 1, 1 );
- delta.addOperation( move );
- delta.addOperation( remove );
- const stringifiedDeltas = JSON.stringify( delta.toJSON() );
- const deltaReplayer = modelDoc.createReplayer( stringifiedDeltas );
- expect( deltaReplayer.getDeltasToReplay() ).to.deep.equal( [ JSON.parse( stringifiedDeltas ) ] );
- } );
- } );
- describe( 'should provide debug tools for delta transformation', () => {
- let document, root, otherRoot;
- beforeEach( () => {
- document = new ModelDocument();
- 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, false );
- 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, true );
- 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: 1
- } );
- } );
- 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, false );
- original = deltaTransform.transform( original[ 0 ], deltaC, true )[ 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 ),
- history[ 0 ].wasImportant
- );
- recreated = recreated[ history[ 0 ].resultIndex ];
- recreated = deltaTransform.transform(
- recreated,
- DeltaFactory.fromJSON( JSON.parse( history[ 1 ].transformedBy ), document ),
- history[ 1 ].wasImportant
- );
- 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 );
- deltaTransform.defaultTransform = () => {
- throw new Error();
- };
- expect( () => deltaTransform.transform( deltaA, deltaB, 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 );
- deltaTransform.defaultTransform = () => {
- throw new Error();
- };
- expect( () => deltaTransform.transform( deltaA, deltaB, 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;
- }
- } );
|