Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #87 from ckeditor/t/86
Browse files Browse the repository at this point in the history
Feature: `Editor#destroy()` will destroy all loaded plugins. Closes #86.

Added default implementation for `Plugin#destroy()`. Introduced `PluginCollection#destroy()` method which calls `Plugin#destroy()` for every loaded plugin.
  • Loading branch information
Reinmar authored Jun 20, 2017
2 parents 82ff39a + 40f33cf commit 77e5217
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export default class Editor {

this.commands.destroy();

return Promise.resolve()
return this.plugins.destroy()
.then( () => {
this.document.destroy();
this.data.destroy();
Expand Down
1 change: 1 addition & 0 deletions src/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default class Plugin {
* @inheritDoc
*/
destroy() {
this.stopListening();
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/plugincollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,20 @@ export default class PluginCollection {
}
}

/**
* Destroys all loaded plugins.
*
* @returns {Promise}
*/
destroy() {
const promises = Array.from( this )
.map( ( [ , pluginInstance ] ) => pluginInstance )
.filter( pluginInstance => typeof pluginInstance.destroy == 'function' )
.map( pluginInstance => pluginInstance.destroy() );

return Promise.all( promises );
}

/**
* Adds the plugin to the collection. Exposed mainly for testing purposes.
*
Expand Down
4 changes: 2 additions & 2 deletions tests/_utils/classictesteditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export default class ClassicTestEditor extends StandardEditor {
destroy() {
this._elementReplacer.restore();

return this.ui.destroy()
.then( () => super.destroy() );
return super.destroy()
.then( () => this.ui.destroy() );
}

/**
Expand Down
2 changes: 2 additions & 0 deletions tests/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,13 @@ describe( 'Editor', () => {

const spy1 = sinon.spy( editor.data, 'destroy' );
const spy2 = sinon.spy( editor.document, 'destroy' );
const spy3 = sinon.spy( editor.plugins, 'destroy' );

return editor.destroy()
.then( () => {
expect( spy1.calledOnce ).to.be.true;
expect( spy2.calledOnce ).to.be.true;
expect( spy3.calledOnce ).to.be.true;
} );
} );
} );
Expand Down
9 changes: 9 additions & 0 deletions tests/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,14 @@ describe( 'constructor()', () => {

expect( plugin.destroy ).to.be.a( 'function' );
} );

it( 'should stop listening', () => {
const plugin = new Plugin( editor );
const stopListeningSpy = sinon.spy( plugin, 'stopListening' );

plugin.destroy();

expect( stopListeningSpy.calledOnce ).to.equal( true );
} );
} );
} );
77 changes: 77 additions & 0 deletions tests/plugincollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* For licensing, see LICENSE.md.
*/

/* globals setTimeout */

import testUtils from '../tests/_utils/utils';
import Editor from '../src/editor/editor';
import PluginCollection from '../src/plugincollection';
Expand Down Expand Up @@ -381,6 +383,81 @@ describe( 'PluginCollection', () => {
} );
} );

describe( 'destroy()', () => {
it( 'calls Plugin#destroy() method on every loaded plugin', () => {
let destroySpyForPluginA, destroySpyForPluginB;

const plugins = new PluginCollection( editor, [] );

return plugins.load( [ PluginA, PluginB ] )
.then( () => {
destroySpyForPluginA = sinon.spy( plugins.get( PluginA ), 'destroy' );
destroySpyForPluginB = sinon.spy( plugins.get( PluginB ), 'destroy' );

return plugins.destroy();
} )
.then( () => {
expect( destroySpyForPluginA.calledOnce ).to.equal( true );
expect( destroySpyForPluginB.calledOnce ).to.equal( true );
} );
} );

it( 'waits until all plugins are destroyed', () => {
const destroyedPlugins = [];

class AsynchronousPluginA extends Plugin {
destroy() {
return new Promise( resolve => {
setTimeout( () => {
super.destroy();

destroyedPlugins.push( 'AsynchronousPluginA.destroy()' );
resolve();
} );
} );
}
}

class AsynchronousPluginB extends Plugin {
destroy() {
return new Promise( resolve => {
setTimeout( () => {
super.destroy();

destroyedPlugins.push( 'AsynchronousPluginB.destroy()' );
resolve();
} );
} );
}
}

const plugins = new PluginCollection( editor, [] );

return plugins.load( [ AsynchronousPluginA, AsynchronousPluginB ] )
.then( () => plugins.destroy() )
.then( () => {
expect( destroyedPlugins ).to.contain( 'AsynchronousPluginB.destroy()' );
expect( destroyedPlugins ).to.contain( 'AsynchronousPluginA.destroy()' );
} );
} );

it( 'does not execute Plugin#destroy() for plugins which do not have this method', () => {
class FooPlugin {
constructor( editor ) {
this.editor = editor;
}
}

const plugins = new PluginCollection( editor, [] );

return plugins.load( [ PluginA, FooPlugin ] )
.then( () => plugins.destroy() )
.then( destroyedPlugins => {
expect( destroyedPlugins.length ).to.equal( 1 );
} );
} );
} );

describe( 'iterator', () => {
it( 'exists', () => {
const plugins = new PluginCollection( editor, availablePlugins );
Expand Down

0 comments on commit 77e5217

Please sign in to comment.