8
0

editor.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: editor, browser-only */
  6. 'use strict';
  7. import moduleUtils from '/tests/ckeditor5/_utils/module.js';
  8. import Editor from '/ckeditor5/editor/editor.js';
  9. import Plugin from '/ckeditor5/plugin.js';
  10. import Config from '/ckeditor5/utils/config.js';
  11. import PluginCollection from '/ckeditor5/plugincollection.js';
  12. const pluginClasses = {};
  13. before( () => {
  14. pluginDefinition( 'A/A' );
  15. pluginDefinition( 'B/B' );
  16. pluginDefinition( 'C/C', [ 'B/B' ] );
  17. pluginDefinition( 'D/D', [ 'C/C' ] );
  18. } );
  19. describe( 'Editor', () => {
  20. describe( 'constructor', () => {
  21. it( 'should create a new editor instance', () => {
  22. const editor = new Editor();
  23. expect( editor.config ).to.be.an.instanceof( Config );
  24. expect( editor.commands ).to.be.an.instanceof( Map );
  25. expect( editor.plugins ).to.be.an.instanceof( PluginCollection );
  26. expect( getPlugins( editor ) ).to.be.empty;
  27. } );
  28. } );
  29. describe( 'plugins', () => {
  30. it( 'should be empty on new editor', () => {
  31. const editor = new Editor();
  32. expect( getPlugins( editor ) ).to.be.empty;
  33. } );
  34. } );
  35. describe( 'create', () => {
  36. it( 'should return a promise that resolves properly', () => {
  37. let promise = Editor.create();
  38. expect( promise ).to.be.an.instanceof( Promise );
  39. return promise;
  40. } );
  41. it( 'loads plugins', () => {
  42. return Editor.create( {
  43. features: [ 'A' ]
  44. } )
  45. .then( editor => {
  46. expect( getPlugins( editor ).length ).to.equal( 1 );
  47. expect( editor.plugins.get( 'A' ) ).to.be.an.instanceof( Plugin );
  48. } );
  49. } );
  50. } );
  51. describe( 'initPlugins', () => {
  52. it( 'should load features', () => {
  53. const editor = new Editor( {
  54. features: [ 'A', 'B' ]
  55. } );
  56. expect( getPlugins( editor ) ).to.be.empty;
  57. return editor.initPlugins().then( () => {
  58. expect( getPlugins( editor ).length ).to.equal( 2 );
  59. expect( editor.plugins.get( 'A' ) ).to.be.an.instanceof( Plugin );
  60. expect( editor.plugins.get( 'B' ) ).to.be.an.instanceof( Plugin );
  61. } );
  62. } );
  63. it( 'should load features passed as a string', () => {
  64. const editor = new Editor( {
  65. features: 'A,B'
  66. } );
  67. expect( getPlugins( editor ) ).to.be.empty;
  68. return editor.initPlugins().then( () => {
  69. expect( getPlugins( editor ).length ).to.equal( 2 );
  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( {
  76. features: [ 'A', 'D' ]
  77. } );
  78. return editor.initPlugins().then( () => {
  79. sinon.assert.callOrder(
  80. editor.plugins.get( pluginClasses[ 'A/A' ] ).init,
  81. editor.plugins.get( pluginClasses[ 'B/B' ] ).init,
  82. editor.plugins.get( pluginClasses[ 'C/C' ] ).init,
  83. editor.plugins.get( pluginClasses[ 'D/D' ] ).init
  84. );
  85. } );
  86. } );
  87. it( 'should initialize plugins in the right order, waiting for asynchronous ones', () => {
  88. class PluginAsync extends Plugin {}
  89. const asyncSpy = sinon.spy().named( 'async-call-spy' );
  90. // Synchronous plugin that depends on an asynchronous one.
  91. pluginDefinition( 'sync/sync', [ 'async/async' ] );
  92. moduleUtils.define( 'async/async', () => {
  93. PluginAsync.prototype.init = sinon.spy( () => {
  94. return new Promise( ( resolve ) => {
  95. setTimeout( () => {
  96. asyncSpy();
  97. resolve();
  98. }, 0 );
  99. } );
  100. } );
  101. return PluginAsync;
  102. } );
  103. const editor = new Editor( {
  104. features: [ 'A', 'sync' ]
  105. } );
  106. return editor.initPlugins().then( () => {
  107. sinon.assert.callOrder(
  108. editor.plugins.get( pluginClasses[ 'A/A' ] ).init,
  109. editor.plugins.get( PluginAsync ).init,
  110. // This one is called with delay by the async init.
  111. asyncSpy,
  112. editor.plugins.get( pluginClasses[ 'sync/sync' ] ).init
  113. );
  114. } );
  115. } );
  116. } );
  117. } );
  118. // @param {String} name Name of the plugin.
  119. // @param {String[]} deps Dependencies of the plugin (only other plugins).
  120. function pluginDefinition( name, deps ) {
  121. moduleUtils.define( name, deps || [], function() {
  122. class NewPlugin extends Plugin {}
  123. NewPlugin.prototype.init = sinon.spy().named( name );
  124. NewPlugin.requires = Array.from( arguments );
  125. pluginClasses[ name ] = NewPlugin;
  126. return NewPlugin;
  127. } );
  128. }
  129. // Returns an array of loaded plugins.
  130. function getPlugins( editor ) {
  131. const plugins = [];
  132. for ( let entry of editor.plugins ) {
  133. // Keep only plugins kept under their classes.
  134. if ( typeof entry[ 0 ] == 'function' ) {
  135. plugins.push( entry[ 1 ] );
  136. }
  137. }
  138. return plugins;
  139. }