editor.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. import amdUtils from '/tests/_utils/amd.js';
  7. import coreTestUtils from '/tests/core/_utils/utils.js';
  8. import Editor from '/ckeditor5/core/editor.js';
  9. import EditorConfig from '/ckeditor5/core/editorconfig.js';
  10. import Plugin from '/ckeditor5/core/plugin.js';
  11. const pluginClasses = {};
  12. let element;
  13. before( () => {
  14. // Define fake plugins to be used in tests.
  15. coreTestUtils.defineEditorCreatorMock( 'test', {
  16. init: sinon.spy().named( 'creator-test' )
  17. } );
  18. pluginDefinition( 'A' );
  19. pluginDefinition( 'B' );
  20. pluginDefinition( 'C', [ 'B' ] );
  21. pluginDefinition( 'D', [ 'C' ] );
  22. } );
  23. beforeEach( () => {
  24. element = document.createElement( 'div' );
  25. document.body.appendChild( element );
  26. } );
  27. ///////////////////
  28. describe( 'constructor', () => {
  29. it( 'should create a new editor instance', () => {
  30. const editor = new Editor( element );
  31. expect( editor ).to.have.property( 'element' ).to.equal( element );
  32. } );
  33. } );
  34. describe( 'config', () => {
  35. it( 'should be an instance of EditorConfig', () => {
  36. const editor = new Editor( element );
  37. expect( editor.config ).to.be.an.instanceof( EditorConfig );
  38. } );
  39. } );
  40. describe( 'init', () => {
  41. it( 'should return a promise that resolves properly', () => {
  42. const editor = new Editor( element, {
  43. creator: 'creator-test'
  44. } );
  45. let promise = editor.init();
  46. expect( promise ).to.be.an.instanceof( Promise );
  47. return promise;
  48. } );
  49. it( 'should load features and creator', () => {
  50. const editor = new Editor( element, {
  51. features: [ 'A', 'B' ],
  52. creator: 'creator-test'
  53. } );
  54. expect( getPlugins( editor ) ).to.be.empty;
  55. return editor.init().then( () => {
  56. expect( getPlugins( editor ).length ).to.equal( 3 );
  57. expect( editor.plugins.get( 'A' ) ).to.be.an.instanceof( Plugin );
  58. expect( editor.plugins.get( 'B' ) ).to.be.an.instanceof( Plugin );
  59. expect( editor.plugins.get( 'creator-test' ) ).to.be.an.instanceof( Plugin );
  60. } );
  61. } );
  62. it( 'should load features passed as a string', () => {
  63. const editor = new Editor( element, {
  64. features: 'A,B',
  65. creator: 'creator-test'
  66. } );
  67. expect( getPlugins( editor ) ).to.be.empty;
  68. return editor.init().then( () => {
  69. expect( getPlugins( editor ).length ).to.equal( 3 );
  70. expect( editor.plugins.get( 'A' ) ).to.be.an.instanceof( Plugin );
  71. expect( editor.plugins.get( 'B' ) ).to.be.an.instanceof( Plugin );
  72. } );
  73. } );
  74. it( 'should initialize plugins in the right order', () => {
  75. const editor = new Editor( element, {
  76. features: [ 'A', 'D' ],
  77. creator: 'creator-test'
  78. } );
  79. return editor.init().then( () => {
  80. sinon.assert.callOrder(
  81. editor.plugins.get( 'creator-test' ).init,
  82. editor.plugins.get( pluginClasses.A ).init,
  83. editor.plugins.get( pluginClasses.B ).init,
  84. editor.plugins.get( pluginClasses.C ).init,
  85. editor.plugins.get( pluginClasses.D ).init
  86. );
  87. } );
  88. } );
  89. it( 'should initialize plugins in the right order, waiting for asynchronous ones', () => {
  90. class PluginAsync extends Plugin {}
  91. const asyncSpy = sinon.spy().named( 'async-call-spy' );
  92. // Synchronous plugin that depends on an asynchronous one.
  93. pluginDefinition( 'sync', [ 'async' ] );
  94. amdUtils.define( 'async', () => {
  95. PluginAsync.prototype.init = sinon.spy( () => {
  96. return new Promise( ( resolve ) => {
  97. setTimeout( () => {
  98. asyncSpy();
  99. resolve();
  100. }, 0 );
  101. } );
  102. } );
  103. return PluginAsync;
  104. } );
  105. const editor = new Editor( element, {
  106. features: [ 'A', 'sync' ],
  107. creator: 'creator-test'
  108. } );
  109. return editor.init().then( () => {
  110. sinon.assert.callOrder(
  111. editor.plugins.get( 'creator-test' ).init,
  112. editor.plugins.get( pluginClasses.A ).init,
  113. editor.plugins.get( PluginAsync ).init,
  114. // This one is called with delay by the async init.
  115. asyncSpy,
  116. editor.plugins.get( pluginClasses.sync ).init
  117. );
  118. } );
  119. } );
  120. } );
  121. describe( 'plugins', () => {
  122. it( 'should be empty on new editor', () => {
  123. const editor = new Editor( element );
  124. expect( getPlugins( editor ) ).to.be.empty;
  125. } );
  126. } );
  127. describe( 'destroy', () => {
  128. it( 'should fire "destroy"', () => {
  129. const editor = new Editor( element );
  130. let spy = sinon.spy();
  131. editor.on( 'destroy', spy );
  132. return editor.destroy().then( () => {
  133. sinon.assert.called( spy );
  134. } );
  135. } );
  136. it( 'should delete the "element" property', () => {
  137. const editor = new Editor( element );
  138. return editor.destroy().then( () => {
  139. expect( editor ).to.not.have.property( 'element' );
  140. } );
  141. } );
  142. } );
  143. /**
  144. * @param {String} name Name of the plugin.
  145. * @param {String[]} deps Dependencies of the plugin (only other plugins).
  146. */
  147. function pluginDefinition( name, deps ) {
  148. amdUtils.define( name, deps || [], function() {
  149. class NewPlugin extends Plugin {}
  150. NewPlugin.prototype.init = sinon.spy().named( name );
  151. NewPlugin.requires = Array.from( arguments );
  152. pluginClasses[ name ] = NewPlugin;
  153. return NewPlugin;
  154. } );
  155. }
  156. /**
  157. * Returns an array of loaded plugins.
  158. */
  159. function getPlugins( editor ) {
  160. const plugins = [];
  161. for ( let entry of editor.plugins ) {
  162. // Keep only plugins kept under their classes.
  163. if ( typeof entry[ 0 ] == 'function' ) {
  164. plugins.push( entry[ 1 ] );
  165. }
  166. }
  167. return plugins;
  168. }