8
0

integration.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* global document */
  6. import BlockQuote from '../src/blockquote';
  7. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  8. import Image from '@ckeditor/ckeditor5-image/src/image';
  9. import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
  10. import List from '@ckeditor/ckeditor5-list/src/list';
  11. import Enter from '@ckeditor/ckeditor5-enter/src/enter';
  12. import Delete from '@ckeditor/ckeditor5-typing/src/delete';
  13. import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
  14. import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  15. describe( 'BlockQuote', () => {
  16. let editor, doc, element;
  17. beforeEach( () => {
  18. element = document.createElement( 'div' );
  19. document.body.appendChild( element );
  20. return ClassicTestEditor.create( element, {
  21. plugins: [ BlockQuote, Paragraph, Image, ImageCaption, List, Enter, Delete ]
  22. } )
  23. .then( newEditor => {
  24. editor = newEditor;
  25. doc = editor.document;
  26. } );
  27. } );
  28. afterEach( () => {
  29. element.remove();
  30. return editor.destroy();
  31. } );
  32. describe( 'enter key support', () => {
  33. function fakeEventData() {
  34. return {
  35. preventDefault: sinon.spy()
  36. };
  37. }
  38. it( 'does nothing if selection is in an empty block but not in a block quote', () => {
  39. const data = fakeEventData();
  40. const execSpy = sinon.spy( editor, 'execute' );
  41. setModelData( doc, '<paragraph>x</paragraph><paragraph>[]</paragraph><paragraph>x</paragraph>' );
  42. editor.editing.view.fire( 'enter', data );
  43. // Only enter command should be executed.
  44. expect( data.preventDefault.called ).to.be.true;
  45. expect( execSpy.calledOnce ).to.be.true;
  46. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
  47. } );
  48. it( 'does nothing if selection is in a non-empty block (at the end) in a block quote', () => {
  49. const data = fakeEventData();
  50. const execSpy = sinon.spy( editor, 'execute' );
  51. setModelData( doc, '<blockQuote><paragraph>xx[]</paragraph></blockQuote>' );
  52. editor.editing.view.fire( 'enter', data );
  53. // Only enter command should be executed.
  54. expect( data.preventDefault.called ).to.be.true;
  55. expect( execSpy.calledOnce ).to.be.true;
  56. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
  57. } );
  58. it( 'does nothing if selection is in a non-empty block (at the beginning) in a block quote', () => {
  59. const data = fakeEventData();
  60. const execSpy = sinon.spy( editor, 'execute' );
  61. setModelData( doc, '<blockQuote><paragraph>[]xx</paragraph></blockQuote>' );
  62. editor.editing.view.fire( 'enter', data );
  63. // Only enter command should be executed.
  64. expect( data.preventDefault.called ).to.be.true;
  65. expect( execSpy.calledOnce ).to.be.true;
  66. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
  67. } );
  68. it( 'does nothing if selection is not collapsed', () => {
  69. const data = fakeEventData();
  70. const execSpy = sinon.spy( editor, 'execute' );
  71. setModelData( doc, '<blockQuote><paragraph>[</paragraph><paragraph>]</paragraph></blockQuote>' );
  72. editor.editing.view.fire( 'enter', data );
  73. // Only enter command should be executed.
  74. expect( data.preventDefault.called ).to.be.true;
  75. expect( execSpy.calledOnce ).to.be.true;
  76. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'enter' );
  77. } );
  78. it( 'does not interfere with a similar handler in the list feature', () => {
  79. const data = fakeEventData();
  80. setModelData( doc,
  81. '<paragraph>x</paragraph>' +
  82. '<blockQuote>' +
  83. '<listItem indent="0" type="bulleted">a</listItem>' +
  84. '<listItem indent="0" type="bulleted">[]</listItem>' +
  85. '</blockQuote>' +
  86. '<paragraph>x</paragraph>'
  87. );
  88. editor.editing.view.fire( 'enter', data );
  89. expect( data.preventDefault.called ).to.be.true;
  90. expect( getModelData( doc ) ).to.equal(
  91. '<paragraph>x</paragraph>' +
  92. '<blockQuote>' +
  93. '<listItem indent="0" type="bulleted">a</listItem>' +
  94. '<paragraph>[]</paragraph>' +
  95. '</blockQuote>' +
  96. '<paragraph>x</paragraph>'
  97. );
  98. } );
  99. it( 'escapes block quote if selection is in an empty block in an empty block quote', () => {
  100. const data = fakeEventData();
  101. const execSpy = sinon.spy( editor, 'execute' );
  102. setModelData( doc, '<paragraph>x</paragraph><blockQuote><paragraph>[]</paragraph></blockQuote><paragraph>x</paragraph>' );
  103. editor.editing.view.fire( 'enter', data );
  104. expect( data.preventDefault.called ).to.be.true;
  105. expect( execSpy.calledOnce ).to.be.true;
  106. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
  107. expect( getModelData( doc ) ).to.equal( '<paragraph>x</paragraph><paragraph>[]</paragraph><paragraph>x</paragraph>' );
  108. } );
  109. it( 'escapes block quote if selection is in an empty block in the middle of a block quote', () => {
  110. const data = fakeEventData();
  111. const execSpy = sinon.spy( editor, 'execute' );
  112. setModelData( doc,
  113. '<paragraph>x</paragraph>' +
  114. '<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph><paragraph>b</paragraph></blockQuote>' +
  115. '<paragraph>x</paragraph>'
  116. );
  117. editor.editing.view.fire( 'enter', data );
  118. expect( data.preventDefault.called ).to.be.true;
  119. expect( execSpy.calledOnce ).to.be.true;
  120. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
  121. expect( getModelData( doc ) ).to.equal(
  122. '<paragraph>x</paragraph>' +
  123. '<blockQuote><paragraph>a</paragraph></blockQuote>' +
  124. '<paragraph>[]</paragraph>' +
  125. '<blockQuote><paragraph>b</paragraph></blockQuote>' +
  126. '<paragraph>x</paragraph>'
  127. );
  128. } );
  129. it( 'escapes block quote if selection is in an empty block at the end of a block quote', () => {
  130. const data = fakeEventData();
  131. const execSpy = sinon.spy( editor, 'execute' );
  132. setModelData( doc,
  133. '<paragraph>x</paragraph>' +
  134. '<blockQuote><paragraph>a</paragraph><paragraph>[]</paragraph></blockQuote>' +
  135. '<paragraph>x</paragraph>'
  136. );
  137. editor.editing.view.fire( 'enter', data );
  138. expect( data.preventDefault.called ).to.be.true;
  139. expect( execSpy.calledOnce ).to.be.true;
  140. expect( execSpy.args[ 0 ][ 0 ] ).to.equal( 'blockQuote' );
  141. expect( getModelData( doc ) ).to.equal(
  142. '<paragraph>x</paragraph>' +
  143. '<blockQuote><paragraph>a</paragraph></blockQuote>' +
  144. '<paragraph>[]</paragraph>' +
  145. '<paragraph>x</paragraph>'
  146. );
  147. } );
  148. } );
  149. describe( 'backspace key support', () => {
  150. function fakeEventData() {
  151. return {
  152. preventDefault: sinon.spy(),
  153. direction: 'backward',
  154. unit: 'character'
  155. };
  156. }
  157. it( 'merges paragraph into paragraph in the quote', () => {
  158. const data = fakeEventData();
  159. setModelData( doc,
  160. '<blockQuote><paragraph>a</paragraph><paragraph>b</paragraph></blockQuote>' +
  161. '<paragraph>[]c</paragraph>' +
  162. '<paragraph>d</paragraph>'
  163. );
  164. editor.editing.view.fire( 'delete', data );
  165. expect( getModelData( doc ) ).to.equal(
  166. '<blockQuote><paragraph>a</paragraph><paragraph>b[]c</paragraph></blockQuote>' +
  167. '<paragraph>d</paragraph>'
  168. );
  169. } );
  170. it( 'merges paragraph from a quote into a paragraph before quote', () => {
  171. const data = fakeEventData();
  172. setModelData( doc,
  173. '<paragraph>x</paragraph>' +
  174. '<blockQuote><paragraph>[]a</paragraph><paragraph>b</paragraph></blockQuote>' +
  175. '<paragraph>y</paragraph>'
  176. );
  177. editor.editing.view.fire( 'delete', data );
  178. expect( getModelData( doc ) ).to.equal(
  179. '<paragraph>x[]a</paragraph>' +
  180. '<blockQuote><paragraph>b</paragraph></blockQuote>' +
  181. '<paragraph>y</paragraph>'
  182. );
  183. } );
  184. it( 'merges two quotes', () => {
  185. const data = fakeEventData();
  186. setModelData( doc,
  187. '<paragraph>x</paragraph>' +
  188. '<blockQuote><paragraph>a</paragraph><paragraph>b</paragraph></blockQuote>' +
  189. '<blockQuote><paragraph>[]c</paragraph><paragraph>d</paragraph></blockQuote>' +
  190. '<paragraph>y</paragraph>'
  191. );
  192. editor.editing.view.fire( 'delete', data );
  193. expect( getModelData( doc ) ).to.equal(
  194. '<paragraph>x</paragraph>' +
  195. '<blockQuote><paragraph>a</paragraph><paragraph>b[]c</paragraph><paragraph>d</paragraph></blockQuote>' +
  196. '<paragraph>y</paragraph>'
  197. );
  198. } );
  199. it( 'removes empty quote when merging into another quote', () => {
  200. const data = fakeEventData();
  201. setModelData( doc,
  202. '<paragraph>x</paragraph>' +
  203. '<blockQuote><paragraph>a</paragraph></blockQuote>' +
  204. '<blockQuote><paragraph>[]</paragraph></blockQuote>' +
  205. '<paragraph>y</paragraph>'
  206. );
  207. editor.editing.view.fire( 'delete', data );
  208. expect( getModelData( doc ) ).to.equal(
  209. '<paragraph>x</paragraph>' +
  210. '<blockQuote><paragraph>a[]</paragraph></blockQuote>' +
  211. '<paragraph>y</paragraph>'
  212. );
  213. } );
  214. it( 'removes empty quote when merging into a paragraph', () => {
  215. const data = fakeEventData();
  216. setModelData( doc,
  217. '<paragraph>x</paragraph>' +
  218. '<blockQuote><paragraph>[]</paragraph></blockQuote>' +
  219. '<paragraph>y</paragraph>'
  220. );
  221. editor.editing.view.fire( 'delete', data );
  222. expect( getModelData( doc ) ).to.equal(
  223. '<paragraph>x[]</paragraph>' +
  224. '<paragraph>y</paragraph>'
  225. );
  226. } );
  227. } );
  228. describe( 'compatibility with images', () => {
  229. it( 'does not quote a simple image', () => {
  230. const element = document.createElement( 'div' );
  231. document.body.appendChild( element );
  232. // We can't load ImageCaption in this test because it adds <caption> to all images automatically.
  233. return ClassicTestEditor.create( element, {
  234. plugins: [ BlockQuote, Paragraph, Image ]
  235. } )
  236. .then( ( editor ) => {
  237. setModelData( editor.document,
  238. '<paragraph>fo[o</paragraph>' +
  239. '<image src="foo.png"></image>' +
  240. '<paragraph>b]ar</paragraph>'
  241. );
  242. editor.execute( 'blockQuote' );
  243. expect( getModelData( editor.document ) ).to.equal(
  244. '<blockQuote><paragraph>fo[o</paragraph></blockQuote>' +
  245. '<image src="foo.png"></image>' +
  246. '<blockQuote><paragraph>b]ar</paragraph></blockQuote>'
  247. );
  248. element.remove();
  249. editor.destroy();
  250. } );
  251. } );
  252. it( 'does not quote an image with caption', () => {
  253. setModelData( doc,
  254. '<paragraph>fo[o</paragraph>' +
  255. '<image src="foo.png">' +
  256. '<caption>xxx</caption>' +
  257. '</image>' +
  258. '<paragraph>b]ar</paragraph>'
  259. );
  260. editor.execute( 'blockQuote' );
  261. expect( getModelData( doc ) ).to.equal(
  262. '<blockQuote><paragraph>fo[o</paragraph></blockQuote>' +
  263. '<image src="foo.png">' +
  264. '<caption>xxx</caption>' +
  265. '</image>' +
  266. '<blockQuote><paragraph>b]ar</paragraph></blockQuote>'
  267. );
  268. } );
  269. it( 'does not add an image to existing quote', () => {
  270. setModelData( doc,
  271. '<paragraph>fo[o</paragraph>' +
  272. '<image src="foo.png">' +
  273. '<caption>xxx</caption>' +
  274. '</image>' +
  275. '<blockQuote><paragraph>b]ar</paragraph></blockQuote>'
  276. );
  277. editor.execute( 'blockQuote' );
  278. expect( getModelData( doc ) ).to.equal(
  279. '<blockQuote><paragraph>fo[o</paragraph></blockQuote>' +
  280. '<image src="foo.png">' +
  281. '<caption>xxx</caption>' +
  282. '</image>' +
  283. '<blockQuote><paragraph>b]ar</paragraph></blockQuote>'
  284. );
  285. } );
  286. } );
  287. } );