Bladeren bron

Added simple tests for error handling.

Maciej Bukowski 6 jaren geleden
bovenliggende
commit
5dc1fbd7b2
2 gewijzigde bestanden met toevoegingen van 144 en 45 verwijderingen
  1. 52 19
      packages/ckeditor5-watchdog/src/watchdog.js
  2. 92 26
      packages/ckeditor5-watchdog/tests/watchdog.js

+ 52 - 19
packages/ckeditor5-watchdog/src/watchdog.js

@@ -52,17 +52,18 @@ export default class Watchdog {
 
 		/**
 		 * @private
-		 * @member {Function} _creator
+		 * @type {Editor}
 		 */
+		this._editor = null;
 
 		/**
 		 * @private
-		 * @member {Function} _destructor
+		 * @member {Function} _creator
 		 */
 
 		/**
 		 * @private
-		 * @member {Editor} _editor
+		 * @member {Function} _destructor
 		 */
 
 		/**
@@ -76,10 +77,26 @@ export default class Watchdog {
 		 * @private
 		 * @member {Number} _lastDocumentVersion
 		 */
+
+		/**
+		* The editor source element.
+		*
+		* @private
+		* @member {HTMLElement} _element
+		*/
+
+		/**
+		* The editor configuration.
+		*
+		* @private
+		* @member {Object|undefined} _config
+		*/
 	}
 
 	/**
 	 * The current editor instance.
+	 *
+	 * @type {module:core/editor/editor~Editor}
 	 */
 	get editor() {
 		return this._editor;
@@ -113,14 +130,25 @@ export default class Watchdog {
 	restart() {
 		return Promise.resolve()
 			.then( () => this.destroy() )
-			.then( () => this.create() )
-			.then( () => this._editor.setData( this._data ) );
+			.then( () => {
+				const updatedConfig = Object.assign( {}, this._config, {
+					initialData: this._data
+				} );
+
+				return this.create( this._element, updatedConfig );
+			} )
+			.then( () => {
+				this.fire( 'restart' );
+			} );
 	}
 
 	/**
+	 * @param {HTMLElement} element
+	 * @param {module:core/editor/editorconfig~EditorConfig} [config]
+	 *
 	 * @returns {Promise.<Watchdog>}
 	 */
-	create() {
+	create( element, config ) {
 		if ( !this._creator ) {
 			throw new Error( 'The watchdog creator is not defined' );
 		}
@@ -129,29 +157,36 @@ export default class Watchdog {
 			throw new Error( 'The watchdog destructor is not defined.' );
 		}
 
-		return this._creator().then( editor => {
-			this._editor = editor;
+		this._element = element;
+		this._config = config;
 
-			window.addEventListener( 'error', this._boundErrorWatcher );
-			this.listenTo( editor.model.document, 'change:data', this._debouncedSave );
+		return Promise.resolve()
+			.then( () => this._creator( element, config ) )
+			.then( editor => {
+				this._editor = editor;
 
-			this._lastDocumentVersion = editor.model.document.version;
-			this._data = editor.getData();
+				window.addEventListener( 'error', this._boundErrorWatcher );
+				this.listenTo( editor.model.document, 'change:data', this._debouncedSave );
 
-			return this;
-		} );
+				this._lastDocumentVersion = editor.model.document.version;
+				this._data = editor.getData();
+
+				return this;
+			} );
 	}
 
 	/**
 	 * Destroys the current editor.
 	 *
-	 * @return {Promise|undefined}
+	 * @returns {Promise}
 	 */
 	destroy() {
 		window.removeEventListener( 'error', this._boundErrorWatcher );
 		this.stopListening( this._editor.model.document, 'change:data', this._debouncedSave );
 
-		return this._destructor( this._editor );
+		return Promise.resolve()
+			.then( () => this._destructor( this._editor ) )
+			.then( () => this._editor = null );
 	}
 
 	/**
@@ -198,9 +233,7 @@ export default class Watchdog {
 			this.fire( 'error' );
 
 			if ( this.crashes.length <= this._crashNumberLimit ) {
-				this.restart().then( () => {
-					this.fire( 'restart' );
-				} );
+				this.restart();
 			}
 		}
 	}

+ 92 - 26
packages/ckeditor5-watchdog/tests/watchdog.js

@@ -4,20 +4,23 @@
  */
 
 import Watchdog from '../src/watchdog';
+import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 describe( 'Watchdog', () => {
-	describe( 'simple scenarios', () => {
-		it( 'watchdog should expose `create()` and `destroy()` methods', () => {
+	testUtils.createSinonSandbox();
+
+	describe( 'create()', () => {
+		it( 'should create an editor instance', () => {
 			const watchdog = new Watchdog();
-			const FakeEditor = getFakeEditor();
 
-			const editorCreateSpy = sinon.spy( FakeEditor, 'create' );
-			const editorDestroySpy = sinon.spy( FakeEditor.prototype, 'destroy' );
+			const editorCreateSpy = testUtils.sinon.spy( VirtualTestEditor, 'create' );
+			const editorDestroySpy = testUtils.sinon.spy( VirtualTestEditor.prototype, 'destroy' );
 
-			watchdog.setCreator( ( el, config ) => FakeEditor.create( el, config ) );
+			watchdog.setCreator( ( el, config ) => VirtualTestEditor.create( el, config ) );
 			watchdog.setDestructor( editor => editor.destroy() );
 
-			return watchdog.create()
+			return watchdog.create( document.createElement( 'div' ), {} )
 				.then( () => {
 					sinon.assert.calledOnce( editorCreateSpy );
 					sinon.assert.notCalled( editorDestroySpy );
@@ -29,21 +32,19 @@ describe( 'Watchdog', () => {
 					sinon.assert.calledOnce( editorDestroySpy );
 				} )
 		} );
-
 	} );
 
 	describe( 'restart()', () => {
 		it( 'should restart the editor', () => {
 			const watchdog = new Watchdog();
-			const FakeEditor = getFakeEditor();
 
-			const editorCreateSpy = sinon.spy( FakeEditor, 'create' );
-			const editorDestroySpy = sinon.spy( FakeEditor.prototype, 'destroy' );
+			const editorCreateSpy = testUtils.sinon.spy( VirtualTestEditor, 'create' );
+			const editorDestroySpy = testUtils.sinon.spy( VirtualTestEditor.prototype, 'destroy' );
 
-			watchdog.setCreator( ( el, config ) => FakeEditor.create( el, config ) );
+			watchdog.setCreator( ( el, config ) => VirtualTestEditor.create( el, config ) );
 			watchdog.setDestructor( editor => editor.destroy() );
 
-			return watchdog.create()
+			return watchdog.create( document.createElement( 'div' ), {} )
 				.then( () => {
 					sinon.assert.calledOnce( editorCreateSpy );
 					sinon.assert.notCalled( editorDestroySpy );
@@ -57,44 +58,111 @@ describe( 'Watchdog', () => {
 					return watchdog.destroy();
 				} );
 		} );
-	} );
 
-	describe( 'editor', () => {
-		it( 'should be the current editor used by the Watchdog', () => {
+		it( 'should restart the editor with the same data', () => {
 			const watchdog = new Watchdog();
+
+			// VirtualTestEditor doesn't handle the `config.initialData` properly.
 			const FakeEditor = getFakeEditor();
 
 			watchdog.setCreator( ( el, config ) => FakeEditor.create( el, config ) );
 			watchdog.setDestructor( editor => editor.destroy() );
 
-			expect( watchdog.editor ).to.be.undefined;
+			return watchdog.create( document.createElement( 'div' ), { initialData: 'foo' } )
+				.then( () => {
+					expect( watchdog.editor.getData() ).to.equal( 'foo' );
+
+					return watchdog.restart();
+				} )
+				.then( () => {
+					expect( watchdog.editor.getData() ).to.equal( 'foo' );
+
+					return watchdog.destroy();
+				} );
+		} );
+	} );
+
+	describe( 'editor', () => {
+		it( 'should be the current editor instance', () => {
+			const watchdog = new Watchdog();
+
+			watchdog.setCreator( ( el, config ) => VirtualTestEditor.create( el, config ) );
+			watchdog.setDestructor( editor => editor.destroy() );
+
+			expect( watchdog.editor ).to.be.null;
 
 			let oldEditor;
 
-			return watchdog.create()
+			return watchdog.create( document.createElement( 'div' ), {} )
 				.then( () => {
 					oldEditor = watchdog.editor;
-					expect( watchdog.editor ).to.be.instanceOf( FakeEditor );
+					expect( watchdog.editor ).to.be.instanceOf( VirtualTestEditor );
 
 					return watchdog.restart();
 				} )
 				.then( () => {
-					expect( watchdog.editor ).to.be.instanceOf( FakeEditor );
+					expect( watchdog.editor ).to.be.instanceOf( VirtualTestEditor );
 					expect( watchdog.editor ).to.not.equal( oldEditor );
 
 					return watchdog.destroy();
+				} )
+				.then( () => {
+					expect( watchdog.editor ).to.be.null;
 				} );
 		} );
 	} );
+
+	describe( 'error handling', () => {
+		it( 'Watchdog should not restart editor during the initialization', () => {
+			const watchdog = new Watchdog();
+
+			watchdog.setCreator( () => Promise.reject( new Error( 'foo' ) ) );
+			watchdog.setDestructor( () => { } );
+
+			return watchdog.create( document.createElement( 'div' ) ).then(
+				() => { throw new Error( '`watchdog.create()` should throw an error.' ) },
+				err => {
+					expect( err ).to.be.instanceOf( Error );
+					expect( err.message ).to.equal( 'foo' );
+				}
+			);
+		} );
+
+		it( 'Watchdog should not restart editor during the destroy', () => {
+			const watchdog = new Watchdog();
+
+			watchdog.setCreator( el => VirtualTestEditor.create( el ) );
+			watchdog.setDestructor( () => Promise.reject( new Error( 'foo' ) ) );
+
+			return Promise.resolve()
+				.then( () => watchdog.create( document.createElement( 'div' ) ) )
+				.then( () => watchdog.destroy() )
+				.then(
+					() => { throw new Error( '`watchdog.create()` should throw an error.' ) },
+					err => {
+						expect( err ).to.be.instanceOf( Error );
+						expect( err.message ).to.equal( 'foo' );
+					}
+				);
+		} );
+	} );
 } );
 
 function getFakeEditor() {
 	return class FakeEditor {
 		static create( el, config ) {
+			const editor = new this();
+
+			return Promise.resolve()
+				.then( () => editor.create( el, config ) );
+		}
+
+		create( el, config ) {
 			this.el = el;
 			this.config = config;
+			this._data = config.initialData || '';
 
-			return Promise.resolve( new this() );
+			return this;
 		}
 
 		constructor() {
@@ -106,14 +174,12 @@ function getFakeEditor() {
 		}
 
 		getData() {
-			return 'foo';
-		}
-
-		setData() {
-
+			return this._data;
 		}
 
 		destroy() {
+			this.el.remove();
+
 			return Promise.resolve();
 		}
 	}