diff --git a/src/controller/datacontroller.js b/src/controller/datacontroller.js index 5ecbe0e3a..678140bd0 100644 --- a/src/controller/datacontroller.js +++ b/src/controller/datacontroller.js @@ -21,12 +21,6 @@ import { convertText, convertToModelFragment } from '../conversion/view-to-model import ViewDocumentFragment from '../view/documentfragment'; import ModelRange from '../model/range'; -import ModelElement from '../model/element'; - -import insertContent from './insertcontent'; -import deleteContent from './deletecontent'; -import modifySelection from './modifyselection'; -import getSelectedContent from './getselectedcontent'; /** * Controller for the data pipeline. The data pipeline controls how data is retrieved from the document @@ -116,9 +110,6 @@ export default class DataController { this.viewToModel.on( 'text', convertText(), { priority: 'lowest' } ); this.viewToModel.on( 'element', convertToModelFragment(), { priority: 'lowest' } ); this.viewToModel.on( 'documentFragment', convertToModelFragment(), { priority: 'lowest' } ); - - [ 'insertContent', 'deleteContent', 'modifySelection', 'getSelectedContent' ] - .forEach( methodName => this.decorate( methodName ) ); } /** @@ -240,126 +231,6 @@ export default class DataController { * Removes all event listeners set by the DataController. */ destroy() {} - - /** - * See {@link module:engine/controller/insertcontent.insertContent}. - * - * @fires insertContent - * @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert. - * @param {module:engine/model/selection~Selection} selection Selection into which the content should be inserted. - */ - insertContent( content, selection ) { - insertContent( this, content, selection ); - } - - /** - * See {@link module:engine/controller/deletecontent.deleteContent}. - * - * Note: For the sake of predictability, the resulting selection should always be collapsed. - * In cases where a feature wants to modify deleting behavior so selection isn't collapsed - * (e.g. a table feature may want to keep row selection after pressing Backspace), - * then that behavior should be implemented in the view's listener. At the same time, the table feature - * will need to modify this method's behavior too, e.g. to "delete contents and then collapse - * the selection inside the last selected cell" or "delete the row and collapse selection somewhere near". - * That needs to be done in order to ensure that other features which use `deleteContent()` will work well with tables. - * - * @fires deleteContent - * @param {module:engine/model/selection~Selection} selection Selection of which the content should be deleted. - * @param {Object} options See {@link module:engine/controller/deletecontent~deleteContent}'s options. - */ - deleteContent( selection, options ) { - deleteContent( this, selection, options ); - } - - /** - * See {@link module:engine/controller/modifyselection.modifySelection}. - * - * @fires modifySelection - * @param {module:engine/model/selection~Selection} selection The selection to modify. - * @param {Object} options See {@link module:engine/controller/modifyselection~modifySelection}'s options. - */ - modifySelection( selection, options ) { - modifySelection( this, selection, options ); - } - - /** - * See {@link module:engine/controller/getselectedcontent.getSelectedContent}. - * - * @fires module:engine/controller/datacontroller~DataController#getSelectedContent - * @param {module:engine/model/selection~Selection} selection The selection of which content will be retrieved. - * @returns {module:engine/model/documentfragment~DocumentFragment} Document fragment holding the clone of the selected content. - */ - getSelectedContent( selection ) { - return getSelectedContent( this, selection ); - } - - /** - * Checks whether given {@link module:engine/model/range~Range range} or {@link module:engine/model/element~Element element} - * has any content. - * - * Content is any text node or element which is registered in {@link module:engine/model/schema~Schema schema}. - * - * @param {module:engine/model/range~Range|module:engine/model/element~Element} rangeOrElement Range or element to check. - * @returns {Boolean} - */ - hasContent( rangeOrElement ) { - if ( rangeOrElement instanceof ModelElement ) { - rangeOrElement = ModelRange.createIn( rangeOrElement ); - } - - if ( rangeOrElement.isCollapsed ) { - return false; - } - - for ( const item of rangeOrElement.getItems() ) { - // Remember, `TreeWalker` returns always `textProxy` nodes. - if ( item.is( 'textProxy' ) || this.model.schema.objects.has( item.name ) ) { - return true; - } - } - - return false; - } } mix( DataController, ObservableMixin ); - -/** - * Event fired when {@link #insertContent} method is called. - * - * The {@link #insertContent default action of that method} is implemented as a - * listener to this event so it can be fully customized by the features. - * - * @event insertContent - * @param {Array} args The arguments passed to the original method. - */ - -/** - * Event fired when {@link #deleteContent} method is called. - * - * The {@link #deleteContent default action of that method} is implemented as a - * listener to this event so it can be fully customized by the features. - * - * @event deleteContent - * @param {Array} args The arguments passed to the original method. - */ - -/** - * Event fired when {@link #modifySelection} method is called. - * - * The {@link #modifySelection default action of that method} is implemented as a - * listener to this event so it can be fully customized by the features. - * - * @event modifySelection - * @param {Array} args The arguments passed to the original method. - */ - -/** - * Event fired when {@link #getSelectedContent} method is called. - * - * The {@link #getSelectedContent default action of that method} is implemented as a - * listener to this event so it can be fully customized by the features. - * - * @event getSelectedContent - * @param {Array} args The arguments passed to the original method. - */ diff --git a/src/model/document.js b/src/model/document.js index 9aed135de..10746fb36 100644 --- a/src/model/document.js +++ b/src/model/document.js @@ -7,17 +7,12 @@ * @module engine/model/document */ -// Load all basic deltas and transformations, they register themselves. -import './delta/basic-deltas'; -import './delta/basic-transformations'; - import Range from './range'; import Position from './position'; import RootElement from './rootelement'; import History from './history'; import DocumentSelection from './documentselection'; import TreeWalker from './treewalker'; -import deltaTransform from './delta/transform'; import clone from '@ckeditor/ckeditor5-utils/src/lib/lodash/clone'; import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror'; @@ -283,26 +278,6 @@ export default class Document { return null; } - /** - * Transforms two sets of deltas by themselves. Returns both transformed sets. - * - * @param {Array.} deltasA Array with the first set of deltas to transform. These - * deltas are considered more important (than `deltasB`) when resolving conflicts. - * @param {Array.} deltasB Array with the second set of deltas to transform. These - * deltas are considered less important (than `deltasA`) when resolving conflicts. - * @param {Boolean} [useContext=false] When set to `true`, transformation will store and use additional context - * information to guarantee more expected results. Should be used whenever deltas related to already applied - * deltas are transformed (for example when undoing changes). - * @returns {Object} - * @returns {Array.} return.deltasA The first set of deltas transformed - * by the second set of deltas. - * @returns {Array.} return.deltasB The second set of deltas transformed - * by the first set of deltas. - */ - transformDeltas( deltasA, deltasB, useContext = false ) { - return deltaTransform.transformDeltaSets( deltasA, deltasB, useContext ? this : null ); - } - /** * Custom toJSON method to solve child-parent circular dependencies. * diff --git a/src/model/model.js b/src/model/model.js index 579786440..a59884e48 100644 --- a/src/model/model.js +++ b/src/model/model.js @@ -7,6 +7,10 @@ * @module engine/model/model */ +// Load all basic deltas and transformations, they register themselves. +import './delta/basic-deltas'; +import './delta/basic-transformations'; + import Batch from './batch'; import Writer from './writer'; import Schema from './schema'; @@ -14,6 +18,14 @@ import Document from './document'; import MarkerCollection from './markercollection'; import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; +import deltaTransform from './delta/transform'; +import ModelElement from './element'; +import ModelRange from './range'; + +import insertContent from './utils/insertcontent'; +import deleteContent from './utils/deletecontent'; +import modifySelection from './utils/modifyselection'; +import getSelectedContent from './utils/getselectedcontent'; /** * Editors data model class. Model defines all data: either nodes users see in editable roots, grouped as the @@ -54,7 +66,8 @@ export default class Model { */ this.markers = new MarkerCollection(); - this.decorate( 'applyOperation' ); + [ 'insertContent', 'deleteContent', 'modifySelection', 'getSelectedContent', 'applyOperation' ] + .forEach( methodName => this.decorate( methodName ) ); } /** @@ -192,49 +205,189 @@ export default class Model { } /** - * Removes all events listeners set by model instance and destroy Document. + * Transforms two sets of deltas by themselves. Returns both transformed sets. + * + * @param {Array.} deltasA Array with the first set of deltas to transform. These + * deltas are considered more important (than `deltasB`) when resolving conflicts. + * @param {Array.} deltasB Array with the second set of deltas to transform. These + * deltas are considered less important (than `deltasA`) when resolving conflicts. + * @param {Boolean} [useContext=false] When set to `true`, transformation will store and use additional context + * information to guarantee more expected results. Should be used whenever deltas related to already applied + * deltas are transformed (for example when undoing changes). + * @returns {Object} + * @returns {Array.} return.deltasA The first set of deltas transformed + * by the second set of deltas. + * @returns {Array.} return.deltasB The second set of deltas transformed + * by the first set of deltas. */ - destroy() { - this.document.destroy(); - this.stopListening(); + transformDeltas( deltasA, deltasB, useContext = false ) { + return deltaTransform.transformDeltaSets( deltasA, deltasB, useContext ? this.document : null ); } /** - * Fired after leaving each {@link module:engine/model/model~Model#enqueueChange} block or outermost - * {@link module:engine/model/model~Model#change} block. - * Have the same parameters as {@link module:engine/model/model~Model#change}. + * See {@link module:engine/model/utils/insertcontent~insertContent}. * - * @event change + * @fires insertContent + * @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert. + * @param {module:engine/model/selection~Selection} selection Selection into which the content should be inserted. */ + insertContent( content, selection ) { + insertContent( this, content, selection ); + } /** - * Fired when all queued model changes are done. + * See {@link module:engine/model/utils/deletecontent.deleteContent}. * - * @see #change - * @see #enqueueChange - * @event changesDone + * Note: For the sake of predictability, the resulting selection should always be collapsed. + * In cases where a feature wants to modify deleting behavior so selection isn't collapsed + * (e.g. a table feature may want to keep row selection after pressing Backspace), + * then that behavior should be implemented in the view's listener. At the same time, the table feature + * will need to modify this method's behavior too, e.g. to "delete contents and then collapse + * the selection inside the last selected cell" or "delete the row and collapse selection somewhere near". + * That needs to be done in order to ensure that other features which use `deleteContent()` will work well with tables. + * + * @fires deleteContent + * @param {module:engine/model/selection~Selection} selection Selection of which the content should be deleted. + * @param {Object} options See {@link module:engine/model/utils/deletecontent~deleteContent}'s options. */ + deleteContent( selection, options ) { + deleteContent( this, selection, options ); + } /** - * Fired every time any {@link module:engine/model/operation/operation~Operation operation} is applied on the model - * using {@link #applyOperation}. + * See {@link module:engine/model/utils/modifyselection~modifySelection}. * - * Note that this is an internal event for the specific use-cases. You can use it if you need to know about each operation - * applied on the document, but in most cases {@link #change} event which is fired when all changes in a - * {@link module:engine/model/batch~Batch} are applied, is a better choice. - * - * With the high priority operation is validated. + * @fires modifySelection + * @param {module:engine/model/selection~Selection} selection The selection to modify. + * @param {Object} options See {@link module:engine/model/utils/modifyselection.modifySelection}'s options. + */ + modifySelection( selection, options ) { + modifySelection( this, selection, options ); + } + + /** + * See {@link module:engine/model/utils/getselectedcontent~getSelectedContent}. * - * With the normal priority operation is executed. After that priority you will be able to get additional - * information about the applied changes returned by {@link module:engine/model/operation/operation~Operation#_execute} - * as `evt.return`. + * @fires getSelectedContent + * @param {module:engine/model/selection~Selection} selection The selection of which content will be retrieved. + * @returns {module:engine/model/documentfragment~DocumentFragment} Document fragment holding the clone of the selected content. + */ + getSelectedContent( selection ) { + return getSelectedContent( this, selection ); + } + + /** + * Checks whether given {@link module:engine/model/range~Range range} or {@link module:engine/model/element~Element element} + * has any content. * - * With the low priority the {@link module:engine/model/document~Document} listen on this event and updates its version. + * Content is any text node or element which is registered in {@link module:engine/model/schema~Schema schema}. * - * @event applyOperation - * @param {Array} args Arguments of the `applyOperation` which are an array with a single element: - * {@link module:engine/model/operation/operation~Operation operation}. + * @param {module:engine/model/range~Range|module:engine/model/element~Element} rangeOrElement Range or element to check. + * @returns {Boolean} + */ + hasContent( rangeOrElement ) { + if ( rangeOrElement instanceof ModelElement ) { + rangeOrElement = ModelRange.createIn( rangeOrElement ); + } + + if ( rangeOrElement.isCollapsed ) { + return false; + } + + for ( const item of rangeOrElement.getItems() ) { + // Remember, `TreeWalker` returns always `textProxy` nodes. + if ( item.is( 'textProxy' ) || this.schema.objects.has( item.name ) ) { + return true; + } + } + + return false; + } + + /** + * Removes all events listeners set by model instance and destroy Document. */ + destroy() { + this.document.destroy(); + this.stopListening(); + } } mix( Model, ObservableMixin ); + +/** + * Fired after leaving each {@link module:engine/model/model~Model#enqueueChange} block or outermost + * {@link module:engine/model/model~Model#change} block. + * Have the same parameters as {@link module:engine/model/model~Model#change}. + * + * @event change + */ + +/** + * Fired when all queued model changes are done. + * + * @see #change + * @see #enqueueChange + * @event changesDone + */ + +/** + * Fired every time any {@link module:engine/model/operation/operation~Operation operation} is applied on the model + * using {@link #applyOperation}. + * + * Note that this is an internal event for the specific use-cases. You can use it if you need to know about each operation + * applied on the document, but in most cases {@link #change} event which is fired when all changes in a + * {@link module:engine/model/batch~Batch} are applied, is a better choice. + * + * With the high priority operation is validated. + * + * With the normal priority operation is executed. After that priority you will be able to get additional + * information about the applied changes returned by {@link module:engine/model/operation/operation~Operation#_execute} + * as `evt.return`. + * + * With the low priority the {@link module:engine/model/document~Document} listen on this event and updates its version. + * + * @event applyOperation + * @param {Array} args Arguments of the `applyOperation` which are an array with a single element: + * {@link module:engine/model/operation/operation~Operation operation}. + */ + +/** + * Event fired when {@link #insertContent} method is called. + * + * The {@link #insertContent default action of that method} is implemented as a + * listener to this event so it can be fully customized by the features. + * + * @event insertContent + * @param {Array} args The arguments passed to the original method. + */ + +/** + * Event fired when {@link #deleteContent} method is called. + * + * The {@link #deleteContent default action of that method} is implemented as a + * listener to this event so it can be fully customized by the features. + * + * @event deleteContent + * @param {Array} args The arguments passed to the original method. + */ + +/** + * Event fired when {@link #modifySelection} method is called. + * + * The {@link #modifySelection default action of that method} is implemented as a + * listener to this event so it can be fully customized by the features. + * + * @event modifySelection + * @param {Array} args The arguments passed to the original method. + */ + +/** + * Event fired when {@link #getSelectedContent} method is called. + * + * The {@link #getSelectedContent default action of that method} is implemented as a + * listener to this event so it can be fully customized by the features. + * + * @event getSelectedContent + * @param {Array} args The arguments passed to the original method. + */ diff --git a/src/controller/deletecontent.js b/src/model/utils/deletecontent.js similarity index 94% rename from src/controller/deletecontent.js rename to src/model/utils/deletecontent.js index 7bf12e7f5..74193d2a8 100644 --- a/src/controller/deletecontent.js +++ b/src/model/utils/deletecontent.js @@ -4,17 +4,17 @@ */ /** - * @module engine/controller/deletecontent + * @module engine/model/utils/deletecontent */ -import LivePosition from '../model/liveposition'; -import Position from '../model/position'; -import Range from '../model/range'; +import LivePosition from '../liveposition'; +import Position from '../position'; +import Range from '../range'; /** * Deletes content of the selection and merge siblings. The resulting selection is always collapsed. * - * @param {module:engine/controller/datacontroller~DataController} dataController The data controller in context of which the insertion + * @param {module:engine/model/model~Model} model The model in context of which the insertion * should be performed. * @param {module:engine/model/selection~Selection} selection Selection of which the content should be deleted. * @param {module:engine/model/batch~Batch} batch Batch to which the deltas will be added. @@ -37,14 +37,14 @@ import Range from '../model/range'; * * `^` with the option disabled (`doNotResetEntireContent == false`) * * `^` with enabled (`doNotResetEntireContent == true`). */ -export default function deleteContent( dataController, selection, options = {} ) { +export default function deleteContent( model, selection, options = {} ) { if ( selection.isCollapsed ) { return; } - const schema = dataController.model.schema; + const schema = model.schema; - dataController.model.change( writer => { + model.change( writer => { // 1. Replace the entire content with paragraph. // See: https://github.com/ckeditor/ckeditor5-engine/issues/1012#issuecomment-315017594. if ( !options.doNotResetEntireContent && shouldEntireContentBeReplacedWithParagraph( schema, selection ) ) { diff --git a/src/controller/getselectedcontent.js b/src/model/utils/getselectedcontent.js similarity index 92% rename from src/controller/getselectedcontent.js rename to src/model/utils/getselectedcontent.js index dd5bc9b99..cd20cf906 100644 --- a/src/controller/getselectedcontent.js +++ b/src/model/utils/getselectedcontent.js @@ -4,11 +4,11 @@ */ /** - * @module engine/controller/getselectedcontent + * @module engine/model/utils/getselectedcontent */ -import Range from '../model/range'; -import Position from '../model/position'; +import Range from '../range'; +import Position from '../position'; /** * Gets a clone of the selected content. @@ -21,13 +21,13 @@ import Position from '../model/position'; * * st

se

* - * @param {module:engine/controller/datacontroller~DataController} dataController The data controller in context of which + * @param {module:engine/model/model~Model} model The model in context of which * the selection modification should be performed. * @param {module:engine/model/selection~Selection} selection The selection of which content will be returned. * @returns {module:engine/model/documentfragment~DocumentFragment} */ -export default function getSelectedContent( dataController, selection ) { - return dataController.model.change( writer => { +export default function getSelectedContent( model, selection ) { + return model.change( writer => { const frag = writer.createDocumentFragment(); const range = selection.getFirstRange(); diff --git a/src/controller/insertcontent.js b/src/model/utils/insertcontent.js similarity index 92% rename from src/controller/insertcontent.js rename to src/model/utils/insertcontent.js index 76196678e..204115061 100644 --- a/src/controller/insertcontent.js +++ b/src/model/utils/insertcontent.js @@ -4,36 +4,36 @@ */ /** - * @module engine/controller/insertcontent + * @module engine/model/utils/insertcontent */ -import Position from '../model/position'; -import LivePosition from '../model/liveposition'; -import Element from '../model/element'; -import Range from '../model/range'; +import Position from '../position'; +import LivePosition from '../liveposition'; +import Element from '../element'; +import Range from '../range'; import log from '@ckeditor/ckeditor5-utils/src/log'; /** * Inserts content into the editor (specified selection) as one would expect the paste * functionality to work. * - * **Note:** Use {@link module:engine/controller/datacontroller~DataController#insertContent} instead of this function. + * **Note:** Use {@link module:engine/model/model~Model#insertContent} instead of this function. * This function is only exposed to be reusable in algorithms - * which change the {@link module:engine/controller/datacontroller~DataController#insertContent} + * which change the {@link module:engine/model/model~Model#insertContent} * method's behavior. * - * @param {module:engine/controller/datacontroller~DataController} dataController The data controller in context of which the insertion + * @param {module:engine/model/model~Model} model The model in context of which the insertion * should be performed. * @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert. * @param {module:engine/model/selection~Selection} selection Selection into which the content should be inserted. */ -export default function insertContent( dataController, content, selection ) { - dataController.model.change( writer => { +export default function insertContent( model, content, selection ) { + model.change( writer => { if ( !selection.isCollapsed ) { - dataController.deleteContent( selection ); + model.deleteContent( selection ); } - const insertion = new Insertion( dataController, writer, selection.anchor ); + const insertion = new Insertion( model, writer, selection.anchor ); let nodesToInsert; @@ -75,13 +75,13 @@ export default function insertContent( dataController, content, selection ) { * @private */ class Insertion { - constructor( dataController, writer, position ) { + constructor( model, writer, position ) { /** - * The data controller in context of which the insertion should be performed. + * The model in context of which the insertion should be performed. * - * @member {module:engine/controller/datacontroller~DataController} #dataController + * @member {module:engine/model~Model} #model */ - this.dataController = dataController; + this.model = model; /** * Batch to which deltas will be added. @@ -115,7 +115,7 @@ class Insertion { * * @member {module:engine/model/schema~Schema} #schema */ - this.schema = dataController.model.schema; + this.schema = model.schema; } /** @@ -149,7 +149,7 @@ class Insertion { return Range.createOn( this.nodeToSelect ); } - return this.dataController.model.document.getNearestSelectionRange( this.position ); + return this.model.document.getNearestSelectionRange( this.position ); } /** diff --git a/src/controller/modifyselection.js b/src/model/utils/modifyselection.js similarity index 92% rename from src/controller/modifyselection.js rename to src/model/utils/modifyselection.js index ac36d3275..83f77b380 100644 --- a/src/controller/modifyselection.js +++ b/src/model/utils/modifyselection.js @@ -4,12 +4,12 @@ */ /** - * @module engine/controller/modifyselection + * @module engine/model/utils/modifyselection */ -import Position from '../model/position'; -import TreeWalker from '../model/treewalker'; -import Range from '../model/range'; +import Position from '../position'; +import TreeWalker from '../treewalker'; +import Range from '../range'; import { isInsideSurrogatePair, isInsideCombinedSymbol } from '@ckeditor/ckeditor5-utils/src/unicode'; /** @@ -33,15 +33,15 @@ import { isInsideSurrogatePair, isInsideCombinedSymbol } from '@ckeditor/ckedito * * **Note:** if you extend a forward selection in a backward direction you will in fact shrink it. * - * @param {module:engine/controller/datacontroller~DataController} dataController The data controller in context of which + * @param {module:engine/model/model~Model} model The model in context of which * the selection modification should be performed. * @param {module:engine/model/selection~Selection} selection The selection to modify. * @param {Object} [options] * @param {'forward'|'backward'} [options.direction='forward'] The direction in which the selection should be modified. * @param {'character'|'codePoint'} [options.unit='character'] The unit by which selection should be modified. */ -export default function modifySelection( dataController, selection, options = {} ) { - const schema = dataController.model.schema; +export default function modifySelection( model, selection, options = {} ) { + const schema = model.schema; const isForward = options.direction != 'backward'; const unit = options.unit ? options.unit : 'character'; diff --git a/tests/controller/datacontroller.js b/tests/controller/datacontroller.js index 18d428abe..a051a6276 100644 --- a/tests/controller/datacontroller.js +++ b/tests/controller/datacontroller.js @@ -11,9 +11,6 @@ import buildViewConverter from '../../src/conversion/buildviewconverter'; import buildModelConverter from '../../src/conversion/buildmodelconverter'; import ModelDocumentFragment from '../../src/model/documentfragment'; -import ModelText from '../../src/model/text'; -import ModelRange from '../../src/model/range'; -import ModelSelection from '../../src/model/selection'; import ViewDocumentFragment from '../../src/view/documentfragment'; @@ -336,223 +333,4 @@ describe( 'DataController', () => { expect( data ).to.respondTo( 'destroy' ); } ); } ); - - describe( 'insertContent()', () => { - it( 'should be decorated', () => { - schema.allow( { name: '$text', inside: '$root' } ); // To surpress warnings. - - const spy = sinon.spy(); - - data.on( 'insertContent', spy ); - - data.insertContent( new ModelText( 'a' ), modelDocument.selection ); - - expect( spy.calledOnce ).to.be.true; - } ); - - it( 'should insert content (item)', () => { - schema.registerItem( 'paragraph', '$block' ); - - setData( model, 'fo[]ar' ); - - data.insertContent( new ModelText( 'ob' ), modelDocument.selection ); - - expect( getData( model ) ).to.equal( 'foob[]ar' ); - } ); - - it( 'should insert content (document fragment)', () => { - schema.registerItem( 'paragraph', '$block' ); - - setData( model, 'fo[]ar' ); - - data.insertContent( new ModelDocumentFragment( [ new ModelText( 'ob' ) ] ), modelDocument.selection ); - - expect( getData( model ) ).to.equal( 'foob[]ar' ); - } ); - - it( 'should use parent batch', () => { - schema.registerItem( 'paragraph', '$block' ); - setData( model, '[]' ); - - model.change( writer => { - data.insertContent( new ModelText( 'abc' ), modelDocument.selection ); - expect( writer.batch.deltas ).to.length( 1 ); - } ); - } ); - } ); - - describe( 'deleteContent()', () => { - it( 'should be decorated', () => { - const spy = sinon.spy(); - - data.on( 'deleteContent', spy ); - - data.deleteContent( modelDocument.selection ); - - expect( spy.calledOnce ).to.be.true; - } ); - - it( 'should delete selected content', () => { - schema.registerItem( 'paragraph', '$block' ); - - setData( model, 'fo[ob]ar' ); - - data.deleteContent( modelDocument.selection ); - - expect( getData( model ) ).to.equal( 'fo[]ar' ); - } ); - - it( 'should use parent batch', () => { - schema.registerItem( 'paragraph', '$block' ); - - setData( model, 'fo[ob]ar' ); - - model.change( writer => { - data.deleteContent( modelDocument.selection ); - expect( writer.batch.deltas ).to.length( 1 ); - } ); - } ); - } ); - - describe( 'modifySelection()', () => { - it( 'should be decorated', () => { - schema.registerItem( 'paragraph', '$block' ); - setData( model, 'fo[ob]ar' ); - - const spy = sinon.spy(); - - data.on( 'modifySelection', spy ); - - data.modifySelection( modelDocument.selection ); - - expect( spy.calledOnce ).to.be.true; - } ); - - it( 'should modify a selection', () => { - schema.registerItem( 'paragraph', '$block' ); - - setData( model, 'fo[ob]ar' ); - - data.modifySelection( modelDocument.selection, { direction: 'backward' } ); - - expect( getData( model ) ).to.equal( 'fo[o]bar' ); - } ); - } ); - - describe( 'getSelectedContent()', () => { - it( 'should be decorated', () => { - const spy = sinon.spy(); - const sel = new ModelSelection(); - - data.on( 'getSelectedContent', spy ); - - data.getSelectedContent( sel ); - - expect( spy.calledOnce ).to.be.true; - } ); - - it( 'should return selected content', () => { - schema.registerItem( 'paragraph', '$block' ); - - setData( model, 'fo[ob]ar' ); - - const content = data.getSelectedContent( modelDocument.selection ); - - expect( stringify( content ) ).to.equal( 'ob' ); - } ); - - it( 'should use parent batch', () => { - schema.registerItem( 'paragraph', '$block' ); - - setData( model, 'fo[ob]ar' ); - - model.change( writer => { - data.getSelectedContent( modelDocument.selection ); - expect( writer.batch.deltas ).to.length( 1 ); - } ); - } ); - } ); - - describe( 'hasContent()', () => { - let root; - - beforeEach( () => { - schema.registerItem( 'paragraph', '$block' ); - schema.registerItem( 'div', '$block' ); - schema.allow( { name: '$block', inside: 'div' } ); - schema.registerItem( 'image' ); - schema.allow( { name: 'image', inside: 'div' } ); - schema.objects.add( 'image' ); - - setData( - model, - - '
' + - '' + - '
' + - 'foo' + - '
' + - '' + - '
' - ); - - root = modelDocument.getRoot(); - } ); - - it( 'should return true if given element has text node', () => { - const pFoo = root.getChild( 1 ); - - expect( data.hasContent( pFoo ) ).to.be.true; - } ); - - it( 'should return true if given element has element that is an object', () => { - const divImg = root.getChild( 2 ); - - expect( data.hasContent( divImg ) ).to.be.true; - } ); - - it( 'should return false if given element has no elements', () => { - const pEmpty = root.getChild( 0 ).getChild( 0 ); - - expect( data.hasContent( pEmpty ) ).to.be.false; - } ); - - it( 'should return false if given element has only elements that are not objects', () => { - const divP = root.getChild( 0 ); - - expect( data.hasContent( divP ) ).to.be.false; - } ); - - it( 'should return true if there is a text node in given range', () => { - const range = ModelRange.createFromParentsAndOffsets( root, 1, root, 2 ); - - expect( data.hasContent( range ) ).to.be.true; - } ); - - it( 'should return true if there is a part of text node in given range', () => { - const pFoo = root.getChild( 1 ); - const range = ModelRange.createFromParentsAndOffsets( pFoo, 1, pFoo, 2 ); - - expect( data.hasContent( range ) ).to.be.true; - } ); - - it( 'should return true if there is element that is an object in given range', () => { - const divImg = root.getChild( 2 ); - const range = ModelRange.createFromParentsAndOffsets( divImg, 0, divImg, 1 ); - - expect( data.hasContent( range ) ).to.be.true; - } ); - - it( 'should return false if range is collapsed', () => { - const range = ModelRange.createFromParentsAndOffsets( root, 1, root, 1 ); - - expect( data.hasContent( range ) ).to.be.false; - } ); - - it( 'should return false if range has only elements that are not objects', () => { - const range = ModelRange.createFromParentsAndOffsets( root, 0, root, 1 ); - - expect( data.hasContent( range ) ).to.be.false; - } ); - } ); } ); diff --git a/tests/model/document/document.js b/tests/model/document/document.js index 55a00f1de..9a5efb116 100644 --- a/tests/model/document/document.js +++ b/tests/model/document/document.js @@ -8,8 +8,6 @@ import Document from '../../../src/model/document'; import RootElement from '../../../src/model/rootelement'; import Batch from '../../../src/model/batch'; import Delta from '../../../src/model/delta/delta'; -import NoOperation from '../../../src/model/operation/nooperation'; -import deltaTransform from '../../../src/model/delta/transform'; import Range from '../../../src/model/range'; import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror'; import count from '@ckeditor/ckeditor5-utils/src/count'; @@ -472,44 +470,6 @@ describe( 'Document', () => { } } ); - describe( 'transformDeltas', () => { - it( 'should use deltaTransform.transformDeltaSets', () => { - sinon.spy( deltaTransform, 'transformDeltaSets' ); - - // Prepare some empty-ish deltas so the transformation won't throw an error. - const deltasA = [ new Delta() ]; - deltasA[ 0 ].addOperation( new NoOperation( 0 ) ); - - const deltasB = [ new Delta() ]; - deltasB[ 0 ].addOperation( new NoOperation( 0 ) ); - - doc.transformDeltas( deltasA, deltasB ); - - expect( deltaTransform.transformDeltaSets.calledOnce ).to.be.true; - expect( deltaTransform.transformDeltaSets.calledWith( deltasA, deltasB, null ) ).to.be.true; - - deltaTransform.transformDeltaSets.restore(); - } ); - - it( 'should pass itself to transformDeltaSets if useContext was set to true', () => { - sinon.spy( deltaTransform, 'transformDeltaSets' ); - - // Prepare some empty-ish deltas so the transformation won't throw an error. - const deltasA = [ new Delta() ]; - deltasA[ 0 ].addOperation( new NoOperation( 0 ) ); - - const deltasB = [ new Delta() ]; - deltasB[ 0 ].addOperation( new NoOperation( 0 ) ); - - doc.transformDeltas( deltasA, deltasB, true ); - - expect( deltaTransform.transformDeltaSets.calledOnce ).to.be.true; - expect( deltaTransform.transformDeltaSets.calledWith( deltasA, deltasB, doc ) ).to.be.true; - - deltaTransform.transformDeltaSets.restore(); - } ); - } ); - describe( '_getDefaultRoot()', () => { it( 'should return graveyard root if there are no other roots in the document', () => { expect( doc._getDefaultRoot() ).to.equal( doc.graveyard ); diff --git a/tests/model/model.js b/tests/model/model.js index 7781c1972..865caae1a 100644 --- a/tests/model/model.js +++ b/tests/model/model.js @@ -3,15 +3,27 @@ * For licensing, see LICENSE.md. */ -import Model from '../../src/model/model'; import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin'; +import Model from '../../src/model/model'; +import NoOperation from '../../src/model/operation/nooperation'; +import deltaTransform from '../../src/model/delta/transform'; +import Delta from '../../src/model/delta/delta'; +import ModelText from '../../src/model/text'; +import ModelRange from '../../src/model/range'; +import ModelSelection from '../../src/model/selection'; +import ModelDocumentFragment from '../../src/model/documentfragment'; +import { getData, setData, stringify } from '../../src/dev-utils/model'; describe( 'Model', () => { - let model; - let changes = ''; + let model, schema, changes; beforeEach( () => { model = new Model(); + model.document.createRoot(); + model.document.createRoot( '$root', 'title' ); + + schema = model.schema; + changes = ''; } ); @@ -322,6 +334,263 @@ describe( 'Model', () => { } ); } ); + describe( 'transformDeltas', () => { + it( 'should use deltaTransform.transformDeltaSets', () => { + sinon.spy( deltaTransform, 'transformDeltaSets' ); + + // Prepare some empty-ish deltas so the transformation won't throw an error. + const deltasA = [ new Delta() ]; + deltasA[ 0 ].addOperation( new NoOperation( 0 ) ); + + const deltasB = [ new Delta() ]; + deltasB[ 0 ].addOperation( new NoOperation( 0 ) ); + + model.transformDeltas( deltasA, deltasB ); + + expect( deltaTransform.transformDeltaSets.calledOnce ).to.be.true; + expect( deltaTransform.transformDeltaSets.calledWith( deltasA, deltasB, null ) ).to.be.true; + + deltaTransform.transformDeltaSets.restore(); + } ); + + it( 'should pass itself to transformDeltaSets if useContext was set to true', () => { + sinon.spy( deltaTransform, 'transformDeltaSets' ); + + // Prepare some empty-ish deltas so the transformation won't throw an error. + const deltasA = [ new Delta() ]; + deltasA[ 0 ].addOperation( new NoOperation( 0 ) ); + + const deltasB = [ new Delta() ]; + deltasB[ 0 ].addOperation( new NoOperation( 0 ) ); + + model.transformDeltas( deltasA, deltasB, true ); + + expect( deltaTransform.transformDeltaSets.calledOnce ).to.be.true; + expect( deltaTransform.transformDeltaSets.calledWith( deltasA, deltasB, model.document ) ).to.be.true; + + deltaTransform.transformDeltaSets.restore(); + } ); + } ); + + describe( 'insertContent()', () => { + it( 'should be decorated', () => { + schema.allow( { name: '$text', inside: '$root' } ); // To surpress warnings. + + const spy = sinon.spy(); + + model.on( 'insertContent', spy ); + + model.insertContent( new ModelText( 'a' ), model.document.selection ); + + expect( spy.calledOnce ).to.be.true; + } ); + + it( 'should insert content (item)', () => { + schema.registerItem( 'paragraph', '$block' ); + + setData( model, 'fo[]ar' ); + + model.insertContent( new ModelText( 'ob' ), model.document.selection ); + + expect( getData( model ) ).to.equal( 'foob[]ar' ); + } ); + + it( 'should insert content (document fragment)', () => { + schema.registerItem( 'paragraph', '$block' ); + + setData( model, 'fo[]ar' ); + + model.insertContent( new ModelDocumentFragment( [ new ModelText( 'ob' ) ] ), model.document.selection ); + + expect( getData( model ) ).to.equal( 'foob[]ar' ); + } ); + + it( 'should use parent batch', () => { + schema.registerItem( 'paragraph', '$block' ); + setData( model, '[]' ); + + model.change( writer => { + model.insertContent( new ModelText( 'abc' ), model.document.selection ); + expect( writer.batch.deltas ).to.length( 1 ); + } ); + } ); + } ); + + describe( 'deleteContent()', () => { + it( 'should be decorated', () => { + const spy = sinon.spy(); + + model.on( 'deleteContent', spy ); + + model.deleteContent( model.document.selection ); + + expect( spy.calledOnce ).to.be.true; + } ); + + it( 'should delete selected content', () => { + schema.registerItem( 'paragraph', '$block' ); + + setData( model, 'fo[ob]ar' ); + + model.deleteContent( model.document.selection ); + + expect( getData( model ) ).to.equal( 'fo[]ar' ); + } ); + + it( 'should use parent batch', () => { + schema.registerItem( 'paragraph', '$block' ); + + setData( model, 'fo[ob]ar' ); + + model.change( writer => { + model.deleteContent( model.document.selection ); + expect( writer.batch.deltas ).to.length( 1 ); + } ); + } ); + } ); + + describe( 'modifySelection()', () => { + it( 'should be decorated', () => { + schema.registerItem( 'paragraph', '$block' ); + setData( model, 'fo[ob]ar' ); + + const spy = sinon.spy(); + + model.on( 'modifySelection', spy ); + + model.modifySelection( model.document.selection ); + + expect( spy.calledOnce ).to.be.true; + } ); + + it( 'should modify a selection', () => { + schema.registerItem( 'paragraph', '$block' ); + + setData( model, 'fo[ob]ar' ); + + model.modifySelection( model.document.selection, { direction: 'backward' } ); + + expect( getData( model ) ).to.equal( 'fo[o]bar' ); + } ); + } ); + + describe( 'getSelectedContent()', () => { + it( 'should be decorated', () => { + const spy = sinon.spy(); + const sel = new ModelSelection(); + + model.on( 'getSelectedContent', spy ); + + model.getSelectedContent( sel ); + + expect( spy.calledOnce ).to.be.true; + } ); + + it( 'should return selected content', () => { + schema.registerItem( 'paragraph', '$block' ); + + setData( model, 'fo[ob]ar' ); + + const content = model.getSelectedContent( model.document.selection ); + + expect( stringify( content ) ).to.equal( 'ob' ); + } ); + + it( 'should use parent batch', () => { + schema.registerItem( 'paragraph', '$block' ); + + setData( model, 'fo[ob]ar' ); + + model.change( writer => { + model.getSelectedContent( model.document.selection ); + expect( writer.batch.deltas ).to.length( 1 ); + } ); + } ); + } ); + + describe( 'hasContent()', () => { + let root; + + beforeEach( () => { + schema.registerItem( 'paragraph', '$block' ); + schema.registerItem( 'div', '$block' ); + schema.allow( { name: '$block', inside: 'div' } ); + schema.registerItem( 'image' ); + schema.allow( { name: 'image', inside: 'div' } ); + schema.objects.add( 'image' ); + + setData( + model, + + '
' + + '' + + '
' + + 'foo' + + '
' + + '' + + '
' + ); + + root = model.document.getRoot(); + } ); + + it( 'should return true if given element has text node', () => { + const pFoo = root.getChild( 1 ); + + expect( model.hasContent( pFoo ) ).to.be.true; + } ); + + it( 'should return true if given element has element that is an object', () => { + const divImg = root.getChild( 2 ); + + expect( model.hasContent( divImg ) ).to.be.true; + } ); + + it( 'should return false if given element has no elements', () => { + const pEmpty = root.getChild( 0 ).getChild( 0 ); + + expect( model.hasContent( pEmpty ) ).to.be.false; + } ); + + it( 'should return false if given element has only elements that are not objects', () => { + const divP = root.getChild( 0 ); + + expect( model.hasContent( divP ) ).to.be.false; + } ); + + it( 'should return true if there is a text node in given range', () => { + const range = ModelRange.createFromParentsAndOffsets( root, 1, root, 2 ); + + expect( model.hasContent( range ) ).to.be.true; + } ); + + it( 'should return true if there is a part of text node in given range', () => { + const pFoo = root.getChild( 1 ); + const range = ModelRange.createFromParentsAndOffsets( pFoo, 1, pFoo, 2 ); + + expect( model.hasContent( range ) ).to.be.true; + } ); + + it( 'should return true if there is element that is an object in given range', () => { + const divImg = root.getChild( 2 ); + const range = ModelRange.createFromParentsAndOffsets( divImg, 0, divImg, 1 ); + + expect( model.hasContent( range ) ).to.be.true; + } ); + + it( 'should return false if range is collapsed', () => { + const range = ModelRange.createFromParentsAndOffsets( root, 1, root, 1 ); + + expect( model.hasContent( range ) ).to.be.false; + } ); + + it( 'should return false if range has only elements that are not objects', () => { + const range = ModelRange.createFromParentsAndOffsets( root, 0, root, 1 ); + + expect( model.hasContent( range ) ).to.be.false; + } ); + } ); + describe( 'destroy()', () => { it( 'should destroy document', () => { sinon.spy( model.document, 'destroy' ); diff --git a/tests/controller/deletecontent.js b/tests/model/utils/deletecontent.js similarity index 94% rename from tests/controller/deletecontent.js rename to tests/model/utils/deletecontent.js index 30afdd617..d9eb1ed77 100644 --- a/tests/controller/deletecontent.js +++ b/tests/model/utils/deletecontent.js @@ -3,29 +3,27 @@ * For licensing, see LICENSE.md. */ -import Model from '../../src/model/model'; -import Position from '../../src/model/position'; -import Range from '../../src/model/range'; -import Element from '../../src/model/element'; -import DataController from '../../src/controller/datacontroller'; -import deleteContent from '../../src/controller/deletecontent'; -import { setData, getData } from '../../src/dev-utils/model'; +import Model from '../../../src/model/model'; +import Position from '../../../src/model/position'; +import Range from '../../../src/model/range'; +import Element from '../../../src/model/element'; +import deleteContent from '../../../src/model/utils/deletecontent'; +import { setData, getData } from '../../../src/dev-utils/model'; describe( 'DataController utils', () => { - let model, doc, data; + let model, doc; describe( 'deleteContent', () => { it( 'should use parent batch', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); model.schema.allow( { name: '$text', inside: '$root' } ); setData( model, 'x[abc]x' ); model.change( writer => { - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( writer.batch.deltas ).to.length( 1 ); } ); } ); @@ -35,7 +33,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -60,7 +57,7 @@ describe( 'DataController utils', () => { it( 'deletes single character (backward selection)', () => { setData( model, 'f[o]o', { lastRangeBackward: true } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model ) ).to.equal( 'f[]o' ); } ); @@ -101,7 +98,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -115,7 +111,7 @@ describe( 'DataController utils', () => { it( 'deletes characters (first half has attrs)', () => { setData( model, '<$text bold="true">fo[ob]ar' ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model ) ).to.equal( '<$text bold="true">fo[]ar' ); expect( doc.selection.getAttribute( 'bold' ) ).to.equal( true ); @@ -124,7 +120,7 @@ describe( 'DataController utils', () => { it( 'deletes characters (2nd half has attrs)', () => { setData( model, 'fo[o<$text bold="true">b]ar' ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model ) ).to.equal( 'fo[]<$text bold="true">ar' ); expect( doc.selection.getAttribute( 'bold' ) ).to.undefined; @@ -133,7 +129,7 @@ describe( 'DataController utils', () => { it( 'clears selection attrs when emptied content', () => { setData( model, 'x[<$text bold="true">foo]y' ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model ) ).to.equal( 'x[]y' ); expect( doc.selection.getAttribute( 'bold' ) ).to.undefined; @@ -150,7 +146,7 @@ describe( 'DataController utils', () => { } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model ) ).to.equal( 'x<$text bold="true">a[]by' ); expect( doc.selection.getAttribute( 'bold' ) ).to.equal( true ); @@ -171,7 +167,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -226,7 +221,7 @@ describe( 'DataController utils', () => { { lastRangeBackward: true } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model ) ).to.equal( 'xfo[]ary' ); } ); @@ -262,7 +257,7 @@ describe( 'DataController utils', () => { model.change( writer => { mergeSpy = sinon.spy( writer, 'merge' ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); } ); expect( getData( model ) ).to.equal( 'ab[]' ); @@ -277,7 +272,7 @@ describe( 'DataController utils', () => { model.change( writer => { mergeSpy = sinon.spy( writer, 'merge' ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); } ); expect( getData( model ) ).to.equal( 'ab[]' ); @@ -293,7 +288,7 @@ describe( 'DataController utils', () => { model.change( writer => { mergeSpy = sinon.spy( writer, 'merge' ); moveSpy = sinon.spy( writer, 'move' ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); } ); expect( getData( model ) ).to.equal( 'ab[]gh' ); @@ -346,7 +341,7 @@ describe( 'DataController utils', () => { doc.selection.setRanges( [ range ] ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model ) ) .to.equal( 'xxfo[]aryy' ); @@ -391,7 +386,7 @@ describe( 'DataController utils', () => { doc.selection.setRanges( [ range ] ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model ) ) .to.equal( 'xfooba[]om' ); @@ -434,7 +429,7 @@ describe( 'DataController utils', () => { doc.selection.setRanges( [ range ] ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model ) ) .to.equal( 'fo[]' ); @@ -528,8 +523,6 @@ describe( 'DataController utils', () => { // Special root which allows only blockWidgets inside itself. doc.createRoot( 'restrictedRoot', 'restrictedRoot' ); - data = new DataController( model ); - const schema = model.schema; schema.limits.add( 'restrictedRoot' ); @@ -557,7 +550,7 @@ describe( 'DataController utils', () => { { rootName: 'paragraphRoot' } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model, { rootName: 'paragraphRoot' } ) ) .to.equal( 'x[]z' ); @@ -570,7 +563,7 @@ describe( 'DataController utils', () => { { rootName: 'bodyRoot' } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model, { rootName: 'bodyRoot' } ) ) .to.equal( 'x[]z' ); @@ -583,7 +576,7 @@ describe( 'DataController utils', () => { { rootName: 'bodyRoot' } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model, { rootName: 'bodyRoot' } ) ) .to.equal( 'x[]z' ); @@ -596,7 +589,7 @@ describe( 'DataController utils', () => { { rootName: 'bodyRoot' } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model, { rootName: 'bodyRoot' } ) ) .to.equal( 'x[]z' ); @@ -609,7 +602,7 @@ describe( 'DataController utils', () => { { rootName: 'bodyRoot' } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model, { rootName: 'bodyRoot' } ) ) .to.equal( 'x[]z' ); @@ -622,7 +615,7 @@ describe( 'DataController utils', () => { { rootName: 'bodyRoot' } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model, { rootName: 'bodyRoot' } ) ) .to.equal( '[]' ); @@ -635,7 +628,7 @@ describe( 'DataController utils', () => { { rootName: 'restrictedRoot' } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model, { rootName: 'restrictedRoot' } ) ) .to.equal( '[]' ); @@ -647,7 +640,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -705,7 +697,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -754,7 +745,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -832,7 +822,7 @@ describe( 'DataController utils', () => { { rootName: 'paragraphRoot' } ); - deleteContent( data, doc.selection ); + deleteContent( model, doc.selection ); expect( getData( model, { rootName: 'paragraphRoot' } ) ) .to.equal( 'x[]z' ); @@ -852,7 +842,7 @@ describe( 'DataController utils', () => { it( title, () => { setData( model, input ); - deleteContent( data, doc.selection, options ); + deleteContent( model, doc.selection, options ); expect( getData( model ) ).to.equal( output ); } ); diff --git a/tests/controller/getselectedcontent.js b/tests/model/utils/getselectedcontent.js similarity index 77% rename from tests/controller/getselectedcontent.js rename to tests/model/utils/getselectedcontent.js index a2aa5dbba..7f28d161b 100644 --- a/tests/controller/getselectedcontent.js +++ b/tests/model/utils/getselectedcontent.js @@ -3,27 +3,25 @@ * For licensing, see LICENSE.md. */ -import Model from '../../src/model/model'; -import DataController from '../../src/controller/datacontroller'; -import DocumentFragment from '../../src/model/documentfragment'; -import getSelectedContent from '../../src/controller/getselectedcontent'; -import { setData, stringify } from '../../src/dev-utils/model'; +import Model from '../../../src/model/model'; +import DocumentFragment from '../../../src/model/documentfragment'; +import getSelectedContent from '../../../src/model/utils/getselectedcontent'; +import { setData, stringify } from '../../../src/dev-utils/model'; describe( 'DataController utils', () => { - let model, doc, data; + let model, doc; describe( 'getSelectedContent', () => { it( 'should use parent batch', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); model.schema.allow( { name: '$text', inside: '$root' } ); setData( model, 'x[abc]x' ); model.change( writer => { - getSelectedContent( data, doc.selection ); + getSelectedContent( model, doc.selection ); expect( writer.batch.deltas ).to.length( 1 ); } ); } ); @@ -33,7 +31,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -48,7 +45,7 @@ describe( 'DataController utils', () => { it( 'returns empty fragment for no selection', () => { setData( model, 'abc' ); - const frag = getSelectedContent( data, doc.selection ); + const frag = getSelectedContent( model, doc.selection ); expect( frag ).instanceOf( DocumentFragment ); expect( frag.isEmpty ).to.be.true; @@ -57,7 +54,7 @@ describe( 'DataController utils', () => { it( 'returns empty fragment for empty selection', () => { setData( model, 'a[]bc' ); - const frag = getSelectedContent( data, doc.selection ); + const frag = getSelectedContent( model, doc.selection ); expect( frag ).instanceOf( DocumentFragment ); expect( frag.isEmpty ).to.be.true; @@ -66,7 +63,7 @@ describe( 'DataController utils', () => { it( 'gets one character', () => { setData( model, 'a[b]c' ); - const frag = getSelectedContent( data, doc.selection ); + const frag = getSelectedContent( model, doc.selection ); const content = stringify( frag ); expect( frag ).instanceOf( DocumentFragment ); @@ -76,49 +73,49 @@ describe( 'DataController utils', () => { it( 'gets full text', () => { setData( model, '[abc]' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'abc' ); } ); it( 'gets text with an attribute', () => { setData( model, 'xxx<$text bold="true">a[b]c' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( '<$text bold="true">b' ); } ); it( 'gets text with attributes', () => { setData( model, 'x<$text bold="true">a[b<$text italic="true">c]dx' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( '<$text bold="true">b<$text italic="true">c' ); } ); it( 'gets text with and without attribute', () => { setData( model, '<$text bold="true">a[bc]d' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( '<$text bold="true">bc' ); } ); it( 'gets text and element', () => { setData( model, '[abc]' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'abc' ); } ); it( 'gets one element', () => { setData( model, 'a[]b' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( '' ); } ); it( 'gets multiple elements', () => { setData( model, '[]' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( '' ); } ); } ); @@ -128,7 +125,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -148,63 +144,63 @@ describe( 'DataController utils', () => { it( 'gets one character', () => { setData( model, 'a[b]c' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'b' ); } ); it( 'gets entire paragraph content', () => { setData( model, '[ab]' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'ab' ); } ); it( 'gets two blocks - partial, partial', () => { setData( model, 'a[bcde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'bcde' ); } ); it( 'gets two blocks - full, partial', () => { setData( model, '[abcde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'abcde' ); } ); it( 'gets two blocks - full, partial 2', () => { setData( model, '[abcde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'abcde' ); } ); it( 'gets two blocks - full, partial 3', () => { setData( model, 'x[abcde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'abcde' ); } ); it( 'gets two blocks - full, partial 4', () => { setData( model, '[abcde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'abcde' ); } ); it( 'gets two blocks - partial, full', () => { setData( model, 'a[bcdef]' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'bcdef' ); } ); it( 'gets two blocks - partial, full 2', () => { setData( model, 'a[bcdef]' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'bcdef' ); } ); @@ -212,7 +208,7 @@ describe( 'DataController utils', () => { it( 'gets two blocks - empty, full', () => { setData( model, 'abc[def]' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'def' ); } ); @@ -220,28 +216,28 @@ describe( 'DataController utils', () => { it( 'gets two blocks - partial, empty', () => { setData( model, 'a[bc]def' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'bc' ); } ); it( 'gets three blocks', () => { setData( model, 'a[bcxde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'bcxde' ); } ); it( 'gets block image', () => { setData( model, 'a[Foo]b' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'Foo' ); } ); it( 'gets two blocks', () => { setData( model, 'a[]b' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( '' ); } ); @@ -249,7 +245,7 @@ describe( 'DataController utils', () => { it( 'gets content when multiple text items needs to be removed from the right excess', () => { setData( model, 'a[bc]d<$text bold="true">ef' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ) .to.equal( 'bc' ); } ); @@ -258,7 +254,7 @@ describe( 'DataController utils', () => { it( 'gets content when multiple text items needs to be removed from the left excess', () => { setData( model, 'a<$text bold="true">bc[de]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ) .to.equal( 'de' ); } ); @@ -269,7 +265,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -284,28 +279,28 @@ describe( 'DataController utils', () => { it( 'gets content when ends are equally deeply nested', () => { setData( model, 'xa[bcde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'bcde' ); } ); it( 'gets content when left end nested deeper', () => { setData( model, 'a[bcde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'bcde' ); } ); it( 'gets content when left end nested deeper 2', () => { setData( model, 'a[bcxde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'bcxde' ); } ); it( 'gets content when left end nested deeper 3', () => { setData( model, 'xa[bcde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'bcde' ); } ); @@ -313,21 +308,21 @@ describe( 'DataController utils', () => { it( 'gets content when left end nested deeper 4', () => { setData( model, 'x[abcde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'abcde' ); } ); it( 'gets content when right end nested deeper', () => { setData( model, 'a[bcde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ).to.equal( 'bcde' ); } ); it( 'gets content when both ends nested deeper than the middle element', () => { setData( model, 'a[bcxde]f' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ) .to.equal( 'bcxde' ); } ); @@ -347,7 +342,7 @@ describe( 'DataController utils', () => { '' ); - const content = stringify( getSelectedContent( data, doc.selection ) ); + const content = stringify( getSelectedContent( model, doc.selection ) ); expect( content ) .to.equal( 'arbo' ); } ); diff --git a/tests/controller/insertcontent.js b/tests/model/utils/insertcontent.js similarity index 96% rename from tests/controller/insertcontent.js rename to tests/model/utils/insertcontent.js index 6bc644fca..9b6621da7 100644 --- a/tests/controller/insertcontent.js +++ b/tests/model/utils/insertcontent.js @@ -3,31 +3,28 @@ * For licensing, see LICENSE.md. */ -import Model from '../../src/model/model'; -import DataController from '../../src/controller/datacontroller'; -import insertContent from '../../src/controller/insertcontent'; +import Model from '../../../src/model/model'; +import insertContent from '../../../src/model/utils/insertcontent'; +import DocumentFragment from '../../../src/model/documentfragment'; +import Text from '../../../src/model/text'; +import Element from '../../../src/model/element'; -import DocumentFragment from '../../src/model/documentfragment'; -import Text from '../../src/model/text'; -import Element from '../../src/model/element'; - -import { setData, getData, parse } from '../../src/dev-utils/model'; +import { setData, getData, parse } from '../../../src/dev-utils/model'; describe( 'DataController utils', () => { - let model, doc, data; + let model, doc; describe( 'insertContent', () => { it( 'should use parent batch', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); model.schema.allow( { name: '$text', inside: '$root' } ); setData( model, 'x[]x' ); model.change( writer => { - insertContent( data, new Text( 'a' ), doc.selection ); + insertContent( model, new Text( 'a' ), doc.selection ); expect( writer.batch.deltas ).to.length( 1 ); } ); } ); @@ -36,13 +33,12 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); model.schema.allow( { name: '$text', inside: '$root' } ); setData( model, 'x[]x' ); - insertContent( data, new DocumentFragment( [ new Text( 'a' ) ] ), doc.selection ); + insertContent( model, new DocumentFragment( [ new Text( 'a' ) ] ), doc.selection ); expect( getData( model ) ).to.equal( 'xa[]x' ); } ); @@ -51,13 +47,12 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); model.schema.allow( { name: '$text', inside: '$root' } ); setData( model, 'x[]x' ); - insertContent( data, new Text( 'a' ), doc.selection ); + insertContent( model, new Text( 'a' ), doc.selection ); expect( getData( model ) ).to.equal( 'xa[]x' ); } ); @@ -66,7 +61,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const content = new Element( 'image' ); @@ -76,7 +70,7 @@ describe( 'DataController utils', () => { setData( model, 'foo[]' ); - insertContent( data, content, doc.selection ); + insertContent( model, content, doc.selection ); expect( doc.getRoot().getChild( 0 ).getChild( 1 ) ).to.equal( content ); } ); @@ -86,7 +80,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -217,7 +210,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -289,7 +281,7 @@ describe( 'DataController utils', () => { ] ); setData( model, '[foo]' ); - insertContent( data, content, doc.selection ); + insertContent( model, content, doc.selection ); expect( getData( model ) ).to.equal( 'bar[]' ); } ); @@ -590,7 +582,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -697,7 +688,6 @@ describe( 'DataController utils', () => { model = new Model(); doc = model.document; doc.createRoot(); - data = new DataController( model ); const schema = model.schema; @@ -764,6 +754,6 @@ describe( 'DataController utils', () => { } ); } - insertContent( data, content, doc.selection ); + insertContent( model, content, doc.selection ); } } ); diff --git a/tests/controller/modifyselection.js b/tests/model/utils/modifyselection.js similarity index 89% rename from tests/controller/modifyselection.js rename to tests/model/utils/modifyselection.js index f4f75bcfa..e9d1c2ffc 100644 --- a/tests/controller/modifyselection.js +++ b/tests/model/utils/modifyselection.js @@ -3,19 +3,17 @@ * For licensing, see LICENSE.md. */ -import Model from '../../src/model/model'; -import DataController from '../../src/controller/datacontroller'; -import Selection from '../../src/model/selection'; -import modifySelection from '../../src/controller/modifyselection'; -import { setData, stringify } from '../../src/dev-utils/model'; +import Model from '../../../src/model/model'; +import Selection from '../../../src/model/selection'; +import modifySelection from '../../../src/model/utils/modifyselection'; +import { setData, stringify } from '../../../src/dev-utils/model'; describe( 'DataController utils', () => { - let model, doc, data; + let model, doc; beforeEach( () => { model = new Model(); doc = model.document; - data = new DataController( model ); model.schema.registerItem( 'p', '$block' ); model.schema.registerItem( 'x', '$block' ); @@ -68,7 +66,7 @@ describe( 'DataController utils', () => { it( 'extends one character backward', () => { setData( model, '

fo[]o

', { lastRangeBackward: true } ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

f[o]o

' ); expect( doc.selection.isBackward ).to.true; @@ -83,7 +81,7 @@ describe( 'DataController utils', () => { it( 'extends one character backward (non-collapsed)', () => { setData( model, '

foob[a]r

', { lastRangeBackward: true } ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

foo[ba]r

' ); expect( doc.selection.isBackward ).to.true; @@ -98,7 +96,7 @@ describe( 'DataController utils', () => { it( 'extends to element boundary (backward)', () => { setData( model, '

f[]oo

' ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

[f]oo

' ); expect( doc.selection.isBackward ).to.true; @@ -114,7 +112,7 @@ describe( 'DataController utils', () => { it( 'shrinks backward selection (to collapsed)', () => { setData( model, '

foo[b]ar

', { lastRangeBackward: true } ); - modifySelection( data, doc.selection ); + modifySelection( model, doc.selection ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

foob[]ar

' ); expect( doc.selection.isBackward ).to.false; @@ -129,7 +127,7 @@ describe( 'DataController utils', () => { it( 'unicode support - combining mark backward', () => { setData( model, '

foob̂[]ar

' ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

foo[b̂]ar

' ); expect( doc.selection.isBackward ).to.true; @@ -144,7 +142,7 @@ describe( 'DataController utils', () => { it( 'unicode support - combining mark multiple backward', () => { setData( model, '

foo̻̐ͩ[]bar

' ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

fo[o̻̐ͩ]bar

' ); expect( doc.selection.isBackward ).to.true; @@ -165,7 +163,7 @@ describe( 'DataController utils', () => { it( 'unicode support - surrogate pairs backward', () => { setData( model, '

\uD83D\uDCA9[]

' ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

[\uD83D\uDCA9]

' ); expect( doc.selection.isBackward ).to.true; @@ -182,7 +180,7 @@ describe( 'DataController utils', () => { it( 'extends over boundary of empty elements (backward)', () => { setData( model, '

[]

' ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

[

]

' ); expect( doc.selection.isBackward ).to.true; @@ -197,7 +195,7 @@ describe( 'DataController utils', () => { it( 'extends over boundary of non-empty elements (backward)', () => { setData( model, '

a

[]bcd

' ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

a[

]bcd

' ); expect( doc.selection.isBackward ).to.true; @@ -212,7 +210,7 @@ describe( 'DataController utils', () => { it( 'extends over character after boundary (backward)', () => { setData( model, '

abc[

]d

', { lastRangeBackward: true } ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

ab[c

]d

' ); expect( doc.selection.isBackward ).to.true; @@ -246,7 +244,7 @@ describe( 'DataController utils', () => { it( 'shrinks over boundary of empty elements', () => { setData( model, '

[

]

', { lastRangeBackward: true } ); - modifySelection( data, doc.selection ); + modifySelection( model, doc.selection ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

[]

' ); expect( doc.selection.isBackward ).to.false; @@ -255,7 +253,7 @@ describe( 'DataController utils', () => { it( 'shrinks over boundary of empty elements (backward)', () => { setData( model, '

[

]

' ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

[]

' ); expect( doc.selection.isBackward ).to.false; @@ -264,7 +262,7 @@ describe( 'DataController utils', () => { it( 'shrinks over boundary of non-empty elements', () => { setData( model, '

a[

]b

', { lastRangeBackward: true } ); - modifySelection( data, doc.selection ); + modifySelection( model, doc.selection ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

a

[]b

' ); expect( doc.selection.isBackward ).to.false; @@ -280,7 +278,7 @@ describe( 'DataController utils', () => { it( 'updates selection attributes', () => { setData( model, '

<$text bold="true">foo[b]

' ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

<$text bold="true">foo[]b

' ); expect( doc.selection.getAttribute( 'bold' ) ).to.equal( true ); @@ -341,7 +339,7 @@ describe( 'DataController utils', () => { setData( model, '' ); - modifySelection( data, doc.selection, { unit: 'codePoint' } ); + modifySelection( model, doc.selection, { unit: 'codePoint' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[]' ); } ); @@ -363,7 +361,7 @@ describe( 'DataController utils', () => { it( 'extends one user-perceived character backward - latin letters', () => { setData( model, '

fo[]o

' ); - modifySelection( data, doc.selection, { unit: 'codePoint', direction: 'backward' } ); + modifySelection( model, doc.selection, { unit: 'codePoint', direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

f[o]o

' ); expect( doc.selection.isBackward ).to.true; @@ -383,7 +381,7 @@ describe( 'DataController utils', () => { // Document's selection will throw errors in some test cases (which are correct cases, but only for // non-document selections). const testSelection = Selection.createFromSelection( doc.selection ); - modifySelection( data, testSelection, { unit: 'codePoint', direction: 'backward' } ); + modifySelection( model, testSelection, { unit: 'codePoint', direction: 'backward' } ); expect( stringify( doc.getRoot(), testSelection ) ).to.equal( '

foob[̂]ar

' ); expect( testSelection.isBackward ).to.true; @@ -406,7 +404,7 @@ describe( 'DataController utils', () => { it( 'unicode support surrogate pairs backward', () => { setData( model, '

\uD83D\uDCA9[]

' ); - modifySelection( data, doc.selection, { unit: 'codePoint', direction: 'backward' } ); + modifySelection( model, doc.selection, { unit: 'codePoint', direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '

[\uD83D\uDCA9]

' ); expect( doc.selection.isBackward ).to.true; @@ -447,7 +445,7 @@ describe( 'DataController utils', () => { it( 'extends over object elements - backward', () => { setData( model, '[]', { lastRangeBackward: true } ); - modifySelection( data, doc.selection, { direction: 'backward' } ); + modifySelection( model, doc.selection, { direction: 'backward' } ); expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[]' ); expect( doc.selection.isBackward ).to.true; @@ -549,7 +547,7 @@ describe( 'DataController utils', () => { // Document's selection will throw errors in some test cases (which are correct cases, but only for // non-document selections). const testSelection = Selection.createFromSelection( doc.selection ); - modifySelection( data, testSelection, options ); + modifySelection( model, testSelection, options ); expect( stringify( doc.getRoot(), testSelection ) ).to.equal( output ); } );