8
0

editor.js 6.2 KB

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