imagetextalternative.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  6. import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
  7. import Image from '../src/image';
  8. import ImageToolbar from '../src/imagetoolbar';
  9. import ImageTextAlternative from '../src/imagetextalternative';
  10. import ImageTextAlternativeEngine from '../src/imagetextalternative/imagetextalternativeengine';
  11. import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
  12. import global from '@ckeditor/ckeditor5-utils/src/dom/global';
  13. import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  14. import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
  15. /* global Event */
  16. describe( 'ImageTextAlternative', () => {
  17. let editor, plugin, command, balloonPanel, form;
  18. beforeEach( () => {
  19. const editorElement = global.document.createElement( 'div' );
  20. global.document.body.appendChild( editorElement );
  21. return ClassicTestEditor.create( editorElement, {
  22. plugins: [ ImageTextAlternative, Image ]
  23. } )
  24. .then( newEditor => {
  25. editor = newEditor;
  26. newEditor.editing.view.attachDomRoot( editorElement );
  27. plugin = editor.plugins.get( ImageTextAlternative );
  28. command = editor.commands.get( 'imageTextAlternative' );
  29. balloonPanel = plugin.balloonPanel;
  30. form = plugin.form;
  31. } );
  32. } );
  33. afterEach( () => {
  34. return editor.destroy();
  35. } );
  36. it( 'should be loaded', () => {
  37. expect( plugin ).to.be.instanceOf( ImageTextAlternative );
  38. } );
  39. it( 'should load ImageTextAlternativeEngine plugin', () => {
  40. expect( editor.plugins.get( ImageTextAlternativeEngine ) ).to.be.instanceOf( ImageTextAlternativeEngine );
  41. } );
  42. describe( 'toolbar button', () => {
  43. let button;
  44. beforeEach( () => {
  45. button = editor.ui.componentFactory.create( 'imageTextAlternative' );
  46. } );
  47. it( 'should be registered in component factory', () => {
  48. expect( button ).to.be.instanceOf( ButtonView );
  49. } );
  50. it( 'should have isEnabled property bind to command\'s isEnabled property', () => {
  51. command.isEnabled = true;
  52. expect( button.isEnabled ).to.be.true;
  53. command.isEnabled = false;
  54. expect( button.isEnabled ).to.be.false;
  55. } );
  56. it( 'should show balloon panel on execute', () => {
  57. const spy = sinon.spy( balloonPanel, 'attach' );
  58. setData( editor.document, '[<image src="" alt="foo bar"></image>]' );
  59. button.fire( 'execute' );
  60. sinon.assert.calledOnce( spy );
  61. } );
  62. it( 'should set alt attribute value to textarea and select it', () => {
  63. const spy = sinon.spy( form.lebeledInput, 'select' );
  64. setData( editor.document, '[<image src="" alt="foo bar"></image>]' );
  65. button.fire( 'execute' );
  66. sinon.assert.calledOnce( spy );
  67. expect( plugin.form.lebeledInput.value ).equals( 'foo bar' );
  68. } );
  69. it( 'should set empty text to textarea and select it when there is no alt attribute', () => {
  70. const spy = sinon.spy( form.lebeledInput, 'select' );
  71. setData( editor.document, '[<image src=""></image>]' );
  72. button.fire( 'execute' );
  73. sinon.assert.calledOnce( spy );
  74. expect( plugin.form.lebeledInput.value ).equals( '' );
  75. } );
  76. it( 'should not add button to default image toolbar if image toolbar is not present', () => {
  77. expect( editor.config.get( 'image.defaultToolbar' ) ).to.be.undefined;
  78. } );
  79. it( 'should add button to default image toolbar if toolbar is present', () => {
  80. const editorElement = global.document.createElement( 'div' );
  81. global.document.body.appendChild( editorElement );
  82. return ClassicTestEditor.create( editorElement, {
  83. plugins: [ ImageTextAlternative, ImageToolbar ]
  84. } )
  85. .then( newEditor => {
  86. expect( newEditor.config.get( 'image.defaultToolbar' ) ).to.eql( [ 'imageTextAlternative' ] );
  87. newEditor.destroy();
  88. } );
  89. } );
  90. it( 'should add separator before the button if toolbar is present and already has items', () => {
  91. const editorElement = global.document.createElement( 'div' );
  92. global.document.body.appendChild( editorElement );
  93. const FooPlugin = class extends Plugin {
  94. init() {
  95. this.editor.ui.componentFactory.add( 'foo', () => new ButtonView() );
  96. this.editor.config.get( 'image.defaultToolbar' ).push( 'foo' );
  97. }
  98. };
  99. return ClassicTestEditor.create( editorElement, {
  100. plugins: [ FooPlugin, ImageTextAlternative, ImageToolbar ]
  101. } )
  102. .then( newEditor => {
  103. expect( newEditor.config.get( 'image.defaultToolbar' ) ).to.eql( [ 'foo', '|', 'imageTextAlternative' ] );
  104. newEditor.destroy();
  105. } );
  106. } );
  107. } );
  108. describe( 'balloon panel form', () => {
  109. it( 'should execute command on submit', () => {
  110. const spy = sinon.spy( editor, 'execute' );
  111. form.fire( 'submit' );
  112. sinon.assert.calledOnce( spy );
  113. sinon.assert.calledWithExactly( spy, 'imageTextAlternative', { newValue: form.lebeledInput.inputView.element.value } );
  114. } );
  115. it( 'should detach panel on cancel', () => {
  116. const spy = sinon.spy( balloonPanel, 'detach' );
  117. form.fire( 'cancel' );
  118. sinon.assert.called( spy );
  119. } );
  120. it( 'should show ImageToolbar on cancel if present', () => {
  121. const editorElement = global.document.createElement( 'div' );
  122. global.document.body.appendChild( editorElement );
  123. return ClassicTestEditor.create( editorElement, {
  124. plugins: [ ImageTextAlternative, Image, ImageToolbar ],
  125. image: {
  126. toolbar: [ 'imageTextAlternative' ]
  127. }
  128. } )
  129. .then( newEditor => {
  130. newEditor.editing.view.attachDomRoot( editorElement );
  131. const plugin = newEditor.plugins.get( ImageTextAlternative );
  132. const toolbarPlugin = newEditor.plugins.get( ImageToolbar );
  133. const form = plugin.form;
  134. const spy = sinon.spy( toolbarPlugin, 'show' );
  135. form.fire( 'cancel' );
  136. sinon.assert.called( spy );
  137. } );
  138. } );
  139. describe( 'close listeners', () => {
  140. let hidePanelSpy;
  141. beforeEach( () => {
  142. hidePanelSpy = sinon.spy( balloonPanel, 'detach' );
  143. } );
  144. describe( 'keyboard', () => {
  145. it( 'should close after `ESC` press', () => {
  146. balloonPanel.isVisible = true;
  147. const keyCode = keyCodes.esc;
  148. const event = global.document.createEvent( 'Events' );
  149. event.initEvent( 'keydown', true, true );
  150. event.which = keyCode;
  151. event.keyCode = keyCode;
  152. global.document.dispatchEvent( event );
  153. sinon.assert.called( hidePanelSpy );
  154. } );
  155. } );
  156. describe( 'mouse', () => {
  157. it( 'should close and not focus editable on click outside the panel', () => {
  158. balloonPanel.isVisible = true;
  159. global.document.body.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
  160. sinon.assert.called( hidePanelSpy );
  161. } );
  162. it( 'should not close on click inside the panel', () => {
  163. balloonPanel.isVisible = true;
  164. balloonPanel.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
  165. sinon.assert.notCalled( hidePanelSpy );
  166. } );
  167. } );
  168. } );
  169. } );
  170. describe( 'working with ImageToolbar', () => {
  171. let editor, button, imageToolbarPlugin, plugin;
  172. beforeEach( () => {
  173. const editorElement = global.document.createElement( 'div' );
  174. global.document.body.appendChild( editorElement );
  175. return ClassicTestEditor.create( editorElement, {
  176. plugins: [ ImageTextAlternative, Image, ImageToolbar ],
  177. image: {
  178. toolbar: [ 'imageTextAlternative' ]
  179. }
  180. } )
  181. .then( newEditor => {
  182. editor = newEditor;
  183. editor.editing.view.attachDomRoot( editorElement );
  184. button = newEditor.ui.componentFactory.create( 'imageTextAlternative' );
  185. imageToolbarPlugin = newEditor.plugins.get( ImageToolbar );
  186. plugin = editor.plugins.get( ImageTextAlternative );
  187. } );
  188. } );
  189. afterEach( () => editor.destroy() );
  190. it( 'should hide ImageToolbar when visible', () => {
  191. setData( editor.document, '[<image src="" alt="foo bar"></image>]' );
  192. const spy = sinon.spy( imageToolbarPlugin, 'hide' );
  193. button.fire( 'execute' );
  194. sinon.assert.calledOnce( spy );
  195. } );
  196. it( 'ImageToolbar should not show when text alternative panel is visible', () => {
  197. setData( editor.document, '[<image src="" alt="foo bar"></image>]' );
  198. button.fire( 'execute' );
  199. const spy = sinon.spy( imageToolbarPlugin, 'show' );
  200. editor.editing.view.render();
  201. sinon.assert.notCalled( spy );
  202. } );
  203. it( 'ImageToolbar should show when text alternative panel is not visible', () => {
  204. setData( editor.document, '[<image src="" alt="foo bar"></image>]' );
  205. button.fire( 'execute' );
  206. const spy = sinon.spy( imageToolbarPlugin, 'show' );
  207. plugin.balloonPanel.isVisible = false;
  208. sinon.assert.called( spy );
  209. } );
  210. } );
  211. } );