Browse Source

Documentation and tests for the Creator.

Piotrek Koszuliński 9 năm trước cách đây
mục cha
commit
e576a1f287

+ 59 - 18
packages/ckeditor5-engine/src/creator.js

@@ -15,16 +15,33 @@ import Plugin from './plugin.js';
  */
 
 export default class Creator extends Plugin {
+	/**
+	 * The element used to {@link #_replaceElement replace} the editor element.
+	 *
+	 * @private
+	 * @property {HTMLElement} _elementReplacemenet
+	 */
+
+	/**
+	 * The creator's trigger. This method is called by the editor to finalize
+	 * the editor creation.
+	 *
+	 * @returns {Promise}
+	 */
 	create() {
 		return this.editor.ui.init();
 	}
 
+	/**
+	 * Method called by the editor on its destruction. It should destroy what the creator created.
+	 *
+	 * @returns {Promise}
+	 */
 	destroy() {
 		super.destroy();
 
 		if ( this._elementReplacement ) {
-			this.editor.element.style.display = '';
-			this._elementReplacement.remove();
+			this._restoreElement();
 		}
 
 		const ui = this.editor.ui;
@@ -33,28 +50,18 @@ export default class Creator extends Plugin {
 		return ui.destroy();
 	}
 
+	/**
+	 * Updates the {@link core.Editor#element editor element}'s content with the data.
+	 */
 	updateEditorElement() {
 		Creator.setDataInElement( this.editor.element, this.editor.getData() );
 	}
 
-	loadDataFromEditorElement() {
-		this.editor.setData( Creator.getDataFromElement( this.editor.element ) );
-	}
-
 	/**
-	 * @param {HTMLElement} [newElement]
+	 * Loads the data from the {@link core.Editor#element editor element} to the editable.
 	 */
-	_replaceElement( newElement ) {
-		if ( !newElement ) {
-			newElement = this.editor.ui.view.element;
-		}
-
-		this._elementReplacement = newElement;
-
-		const editorEl = this.editor.element;
-
-		editorEl.style.display = 'none';
-		editorEl.parentNode.insertBefore( newElement, editorEl.nextSibling );
+	loadDataFromEditorElement() {
+		this.editor.setData( Creator.getDataFromElement( this.editor.element ) );
 	}
 
 	/**
@@ -84,4 +91,38 @@ export default class Creator extends Plugin {
 
 		el.innerHTML = data;
 	}
+
+	/**
+	 * Hides the {@link core.Editor#element editor element} and inserts the the given element
+	 * (usually, editor's UI main element) next to it.
+	 *
+	 * The effect of this method will be automatically reverted by {@link #destroy}.
+	 *
+	 * @protected
+	 * @param {HTMLElement} [newElement] The replacement element. If not passed, then the main editor's UI view element
+	 * will be used.
+	 */
+	_replaceElement( newElement ) {
+		if ( !newElement ) {
+			newElement = this.editor.ui.view.element;
+		}
+
+		this._elementReplacement = newElement;
+
+		const editorEl = this.editor.element;
+
+		editorEl.style.display = 'none';
+		editorEl.parentNode.insertBefore( newElement, editorEl.nextSibling );
+	}
+
+	/**
+	 * Restores what the {@link #_replaceElement} did.
+	 *
+	 * @protected
+	 */
+	_restoreElement() {
+		this.editor.element.style.display = '';
+		this._elementReplacement.remove();
+		this._elementReplacement = null;
+	}
 }

+ 151 - 0
packages/ckeditor5-engine/tests/creator/creator.js

@@ -5,9 +5,132 @@
 
 'use strict';
 
+import testUtils from '/tests/_utils/utils.js';
 import Creator from '/ckeditor5/core/creator.js';
+import Editor from '/ckeditor5/core/editor.js';
+import Plugin from '/ckeditor5/core/plugin.js';
+
+testUtils.createSinonSandbox();
 
 describe( 'Creator', () => {
+	let creator, editor;
+
+	beforeEach( () => {
+		const editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+
+		editor = new Editor( editorElement );
+		creator = new Creator( editor );
+	} );
+
+	describe( 'create', () => {
+		it( 'should init the UI', () => {
+			const promise = new Promise( () => {} );
+
+			editor.ui = {
+				init: sinon.stub().returns( promise )
+			};
+
+			const ret = creator.create();
+
+			expect( ret ).to.equal( promise );
+			expect( editor.ui.init.called ).to.be.true;
+		} );
+	} );
+
+	describe( 'destroy', () => {
+		it( 'calls super.destroy', () => {
+			const pluginSpy  = testUtils.sinon.spy( Plugin.prototype, 'destroy' );
+
+			editor.ui = {
+				destroy() {}
+			};
+
+			creator.destroy();
+
+			expect( pluginSpy.called ).to.be.true;
+		} );
+
+		it( 'should destroy the UI', () => {
+			const uiSpy = sinon.stub().returns( new Promise( () => {} ) );
+
+			editor.ui = {
+				destroy: uiSpy
+			};
+
+			creator.destroy();
+
+			expect( uiSpy.called ).to.be.true;
+			expect( editor.ui ).to.be.null;
+		} );
+
+		it( 'should wait until UI is destroyed', () => {
+			let resolved = false;
+			let resolve;
+			const uiSpy = sinon.stub().returns(
+				new Promise( ( r ) => {
+					resolve = r;
+				} )
+			);
+
+			editor.ui = {
+				destroy: uiSpy
+			};
+
+			// Is there an easier method to verify whether the promise chain isn't broken? ;/
+			setTimeout( () => {
+				resolved = true;
+				resolve( 'foo' );
+			} );
+
+			return creator.destroy()
+				.then( () => {
+					expect( resolved ).to.be.true;
+				} );
+		} );
+
+		it( 'should restore the replaced element', () => {
+			const spy = testUtils.sinon.stub( creator, '_restoreElement' );
+			const element = document.createElement( 'div' );
+
+			editor.ui = {
+				destroy() {}
+			};
+
+			creator._replaceElement( element );
+			creator.destroy();
+
+			expect( spy.calledOnce ).to.be.true;
+		} );
+	} );
+
+	describe( 'updateEditorElement', () => {
+		it( 'should pass data to the element', () => {
+			editor.editable = {
+				getData() {
+					return 'foo';
+				}
+			};
+
+			creator.updateEditorElement();
+
+			expect( editor.element.innerHTML ).to.equal( 'foo' );
+		} );
+	} );
+
+	describe( 'loadDataFromEditorElement', () => {
+		it( 'should pass data to the element', () => {
+			editor.editable = {
+				setData: sinon.spy()
+			};
+
+			editor.element.innerHTML = 'foo';
+			creator.loadDataFromEditorElement();
+
+			expect( editor.editable.setData.args[ 0 ][ 0 ] ).to.equal( 'foo' );
+		} );
+	} );
+
 	describe( 'getDataFromElement', () => {
 		[ 'textarea', 'template', 'div' ].forEach( ( elementName ) => {
 			it( 'should return the content of a ' + elementName, function() {
@@ -30,4 +153,32 @@ describe( 'Creator', () => {
 			} );
 		} );
 	} );
+
+	describe( '_replaceElement', () => {
+		it( 'should use editor ui element when arg not provided', () => {
+			editor.ui = {
+				view: {
+					element: document.createElement( 'div' )
+				}
+			};
+
+			creator._replaceElement();
+
+			expect( editor.element.nextSibling ).to.equal( editor.ui.view.element );
+		} );
+	} );
+
+	describe( '_restoreElement', () => {
+		it( 'should remove the replacement element', () => {
+			const element = document.createElement( 'div' );
+
+			creator._replaceElement( element );
+
+			expect( editor.element.nextSibling ).to.equal( element );
+
+			creator._restoreElement();
+
+			expect( element.parentNode ).to.be.null;
+		} );
+	} );
 } );