context.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /**
  2. * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. import Context from '../src/context';
  6. import ContextPlugin from '../src/contextplugin';
  7. import Plugin from '../src/plugin';
  8. import VirtualTestEditor from './_utils/virtualtesteditor';
  9. import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  10. describe( 'Context', () => {
  11. it( 'should share the same instance of plugin within editors using the same context', async () => {
  12. class ContextPluginA extends ContextPlugin {}
  13. class ContextPluginB extends ContextPlugin {}
  14. class EditorPluginA extends Plugin {}
  15. const context = await Context.create( { plugins: [ ContextPluginA, ContextPluginB ] } );
  16. const editorA = await VirtualTestEditor.create( { context, plugins: [ ContextPluginA, EditorPluginA ] } );
  17. const editorB = await VirtualTestEditor.create( { context, plugins: [ ContextPluginB, EditorPluginA ] } );
  18. expect( editorA.plugins.get( ContextPluginA ) ).to.equal( context.plugins.get( ContextPluginA ) );
  19. expect( editorA.plugins.has( ContextPluginB ) ).to.equal( false );
  20. expect( editorB.plugins.get( ContextPluginB ) ).to.equal( context.plugins.get( ContextPluginB ) );
  21. expect( editorB.plugins.has( ContextPluginA ) ).to.equal( false );
  22. expect( context.plugins.has( EditorPluginA ) ).to.equal( false );
  23. expect( editorA.plugins.get( EditorPluginA ) ).to.not.equal( editorB.plugins.get( EditorPluginA ) );
  24. await context.destroy();
  25. } );
  26. it( 'should share the same instance of plugin (dependencies) within editors using the same context', async () => {
  27. class ContextPluginA extends ContextPlugin {}
  28. class ContextPluginB extends ContextPlugin {}
  29. class EditorPluginA extends Plugin {
  30. static get requires() {
  31. return [ ContextPluginA ];
  32. }
  33. }
  34. class EditorPluginB extends Plugin {
  35. static get requires() {
  36. return [ ContextPluginB ];
  37. }
  38. }
  39. const context = await Context.create( { plugins: [ ContextPluginA, ContextPluginB ] } );
  40. const editorA = await VirtualTestEditor.create( { context, plugins: [ EditorPluginA ] } );
  41. const editorB = await VirtualTestEditor.create( { context, plugins: [ EditorPluginB ] } );
  42. expect( context.plugins.get( ContextPluginA ) ).to.equal( editorA.plugins.get( ContextPluginA ) );
  43. expect( context.plugins.get( ContextPluginB ) ).to.equal( editorB.plugins.get( ContextPluginB ) );
  44. await context.destroy();
  45. } );
  46. it( 'should not initialize twice plugin added to the context and the editor', async () => {
  47. const initSpy = sinon.spy();
  48. const afterInitSpy = sinon.spy();
  49. class ContextPluginA extends ContextPlugin {
  50. init() {
  51. initSpy();
  52. }
  53. afterInit() {
  54. afterInitSpy();
  55. }
  56. }
  57. const context = await Context.create( { plugins: [ ContextPluginA ] } );
  58. const editor = await VirtualTestEditor.create( { context, plugins: [ ContextPluginA ] } );
  59. expect( context.plugins.get( ContextPluginA ) ).to.equal( editor.plugins.get( ContextPluginA ) );
  60. sinon.assert.calledOnce( initSpy );
  61. sinon.assert.calledOnce( afterInitSpy );
  62. await context.destroy();
  63. } );
  64. it( 'should not destroy context along with the editor when context is injected to the editor', async () => {
  65. const contextPluginDestroySpy = sinon.spy();
  66. const editorPluginDestroySpy = sinon.spy();
  67. class ContextPluginA extends ContextPlugin {
  68. destroy() {
  69. contextPluginDestroySpy();
  70. }
  71. }
  72. class EditorPlugin extends Plugin {
  73. destroy() {
  74. editorPluginDestroySpy();
  75. }
  76. }
  77. const context = await Context.create( { plugins: [ ContextPluginA ] } );
  78. const editor = await VirtualTestEditor.create( { context, plugins: [ ContextPluginA, EditorPlugin ] } );
  79. await editor.destroy();
  80. sinon.assert.calledOnce( editorPluginDestroySpy );
  81. sinon.assert.notCalled( contextPluginDestroySpy );
  82. } );
  83. it( 'should destroy all editors with injected context when context is destroyed', async () => {
  84. const context = await Context.create();
  85. const editorA = await VirtualTestEditor.create( { context } );
  86. const editorB = await VirtualTestEditor.create( { context } );
  87. const editorC = await VirtualTestEditor.create();
  88. sinon.spy( editorA, 'destroy' );
  89. sinon.spy( editorB, 'destroy' );
  90. sinon.spy( editorC, 'destroy' );
  91. await context.destroy();
  92. sinon.assert.calledOnce( editorA.destroy );
  93. sinon.assert.calledOnce( editorB.destroy );
  94. sinon.assert.notCalled( editorC.destroy );
  95. } );
  96. it( 'should be able to add context plugin to the editor using pluginName property', async () => {
  97. class ContextPluginA extends ContextPlugin {
  98. static get pluginName() {
  99. return 'ContextPluginA';
  100. }
  101. }
  102. class ContextPluginB extends ContextPlugin {
  103. static get pluginName() {
  104. return 'ContextPluginB';
  105. }
  106. static get requires() {
  107. return [ ContextPluginA ];
  108. }
  109. }
  110. const context = await Context.create( { plugins: [ ContextPluginB ] } );
  111. const editor = await VirtualTestEditor.create( { context, plugins: [ 'ContextPluginA' ] } );
  112. expect( editor.plugins.has( ContextPluginA ) ).to.equal( true );
  113. expect( editor.plugins.has( ContextPluginB ) ).to.equal( false );
  114. } );
  115. it( 'should throw when plugin is added to the context by name', async () => {
  116. let caughtError;
  117. try {
  118. await Context.create( { plugins: [ 'ContextPlugin' ] } );
  119. } catch ( error ) {
  120. caughtError = error;
  121. }
  122. expect( caughtError ).to.instanceof( CKEditorError );
  123. expect( caughtError.message ).match( /^context-initplugins: Only constructor is allowed as a Context plugin./ );
  124. } );
  125. it( 'should throw when plugin added to the context is not marked as a ContextPlugin #1', async () => {
  126. class EditorPlugin extends Plugin {}
  127. let caughtError;
  128. try {
  129. await Context.create( { plugins: [ EditorPlugin ] } );
  130. } catch ( error ) {
  131. caughtError = error;
  132. }
  133. expect( caughtError ).to.instanceof( CKEditorError );
  134. expect( caughtError.message ).match( /^context-initplugins: Only plugins marked as a ContextPlugin are allowed./ );
  135. } );
  136. it( 'should throw when plugin added to the context is not marked as a ContextPlugin #2', async () => {
  137. function EditorPlugin() {}
  138. let caughtError;
  139. try {
  140. await Context.create( { plugins: [ EditorPlugin ] } );
  141. } catch ( error ) {
  142. caughtError = error;
  143. }
  144. expect( caughtError ).to.instanceof( CKEditorError );
  145. expect( caughtError.message ).match( /^context-initplugins: Only plugins marked as a ContextPlugin are allowed./ );
  146. } );
  147. it( 'should not throw when plugin added to the context has not a ContextPlugin proto but is marked as a ContextPlugin', async () => {
  148. function EditorPlugin() {}
  149. EditorPlugin.isContextPlugin = true;
  150. let caughtError;
  151. try {
  152. await Context.create( { plugins: [ EditorPlugin ] } );
  153. } catch ( error ) {
  154. caughtError = error;
  155. }
  156. expect( caughtError ).to.equal( undefined );
  157. } );
  158. it( 'should throw when plugin added to the context is not marked as a ContextPlugin #2', async () => {
  159. function EditorPlugin() {}
  160. let caughtError;
  161. try {
  162. await Context.create( { plugins: [ EditorPlugin ] } );
  163. } catch ( error ) {
  164. caughtError = error;
  165. }
  166. expect( caughtError ).to.instanceof( CKEditorError );
  167. expect( caughtError.message ).match( /^context-initplugins: Only plugins marked as a ContextPlugin are allowed./ );
  168. } );
  169. } );