editor.js 6.9 KB

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