autoformat.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import Autoformat from '../src/autoformat';
  6. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  7. import ListEngine from '@ckeditor/ckeditor5-list/src/listengine';
  8. import HeadingEngine from '@ckeditor/ckeditor5-heading/src/headingengine';
  9. import BoldEngine from '@ckeditor/ckeditor5-basic-styles/src/boldengine';
  10. import ItalicEngine from '@ckeditor/ckeditor5-basic-styles/src/italicengine';
  11. import BlockQuoteEngine from '@ckeditor/ckeditor5-block-quote/src/blockquoteengine';
  12. import Enter from '@ckeditor/ckeditor5-enter/src/enter';
  13. import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
  14. import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  15. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  16. import Command from '@ckeditor/ckeditor5-core/src/command';
  17. testUtils.createSinonSandbox();
  18. describe( 'Autoformat', () => {
  19. let editor, doc, batch;
  20. beforeEach( () => {
  21. return VirtualTestEditor
  22. .create( {
  23. plugins: [ Enter, Paragraph, Autoformat, ListEngine, HeadingEngine, BoldEngine, ItalicEngine, BlockQuoteEngine ]
  24. } )
  25. .then( newEditor => {
  26. editor = newEditor;
  27. doc = editor.document;
  28. batch = doc.batch();
  29. } );
  30. } );
  31. afterEach( () => {
  32. return editor.destroy();
  33. } );
  34. describe( 'Bulleted list', () => {
  35. it( 'should replace asterisk with bulleted list item', () => {
  36. setData( doc, '<paragraph>*[]</paragraph>' );
  37. doc.enqueueChanges( () => {
  38. batch.insert( doc.selection.getFirstPosition(), ' ' );
  39. } );
  40. expect( getData( doc ) ).to.equal( '<listItem indent="0" type="bulleted">[]</listItem>' );
  41. } );
  42. it( 'should replace minus character with bulleted list item', () => {
  43. setData( doc, '<paragraph>-[]</paragraph>' );
  44. doc.enqueueChanges( () => {
  45. batch.insert( doc.selection.getFirstPosition(), ' ' );
  46. } );
  47. expect( getData( doc ) ).to.equal( '<listItem indent="0" type="bulleted">[]</listItem>' );
  48. } );
  49. it( 'should not replace minus character when inside bulleted list item', () => {
  50. setData( doc, '<listItem indent="0" type="bulleted">-[]</listItem>' );
  51. doc.enqueueChanges( () => {
  52. batch.insert( doc.selection.getFirstPosition(), ' ' );
  53. } );
  54. expect( getData( doc ) ).to.equal( '<listItem indent="0" type="bulleted">- []</listItem>' );
  55. } );
  56. } );
  57. describe( 'Numbered list', () => {
  58. it( 'should replace digit with numbered list item', () => {
  59. setData( doc, '<paragraph>1.[]</paragraph>' );
  60. doc.enqueueChanges( () => {
  61. batch.insert( doc.selection.getFirstPosition(), ' ' );
  62. } );
  63. expect( getData( doc ) ).to.equal( '<listItem indent="0" type="numbered">[]</listItem>' );
  64. } );
  65. it( 'should not replace digit character when inside numbered list item', () => {
  66. setData( doc, '<listItem indent="0" type="numbered">1.[]</listItem>' );
  67. doc.enqueueChanges( () => {
  68. batch.insert( doc.selection.getFirstPosition(), ' ' );
  69. } );
  70. expect( getData( doc ) ).to.equal( '<listItem indent="0" type="numbered">1. []</listItem>' );
  71. } );
  72. } );
  73. describe( 'Heading', () => {
  74. it( 'should replace hash character with heading', () => {
  75. setData( doc, '<paragraph>#[]</paragraph>' );
  76. doc.enqueueChanges( () => {
  77. batch.insert( doc.selection.getFirstPosition(), ' ' );
  78. } );
  79. expect( getData( doc ) ).to.equal( '<heading1>[]</heading1>' );
  80. } );
  81. it( 'should replace two hash characters with heading level 2', () => {
  82. setData( doc, '<paragraph>##[]</paragraph>' );
  83. doc.enqueueChanges( () => {
  84. batch.insert( doc.selection.getFirstPosition(), ' ' );
  85. } );
  86. expect( getData( doc ) ).to.equal( '<heading2>[]</heading2>' );
  87. } );
  88. it( 'should not replace hash character when inside heading', () => {
  89. setData( doc, '<heading1>#[]</heading1>' );
  90. doc.enqueueChanges( () => {
  91. batch.insert( doc.selection.getFirstPosition(), ' ' );
  92. } );
  93. expect( getData( doc ) ).to.equal( '<heading1># []</heading1>' );
  94. } );
  95. it( 'should work with heading1-heading6 commands regardless of the config of the heading feature', () => {
  96. const spy1 = sinon.spy();
  97. const spy6 = sinon.spy();
  98. class Heading6 extends Command {
  99. execute() {
  100. spy6();
  101. }
  102. }
  103. class Heading1 extends Command {
  104. execute() {
  105. spy1();
  106. }
  107. }
  108. function HeadingPlugin( editor ) {
  109. editor.commands.add( 'heading1', new Heading1( editor ) );
  110. editor.commands.add( 'heading6', new Heading6( editor ) );
  111. }
  112. return VirtualTestEditor
  113. .create( {
  114. plugins: [
  115. Paragraph, Autoformat, HeadingPlugin
  116. ]
  117. } )
  118. .then( editor => {
  119. const doc = editor.document;
  120. setData( doc, '<paragraph>#[]</paragraph>' );
  121. doc.enqueueChanges( () => {
  122. doc.batch().insert( doc.selection.getFirstPosition(), ' ' );
  123. } );
  124. expect( spy1.calledOnce ).to.be.true;
  125. setData( doc, '<paragraph>######[]</paragraph>' );
  126. doc.enqueueChanges( () => {
  127. doc.batch().insert( doc.selection.getFirstPosition(), ' ' );
  128. } );
  129. expect( spy6.calledOnce ).to.be.true;
  130. return editor.destroy();
  131. } );
  132. } );
  133. } );
  134. describe( 'Block quote', () => {
  135. it( 'should replace greater-than character with heading', () => {
  136. setData( doc, '<paragraph>>[]</paragraph>' );
  137. doc.enqueueChanges( () => {
  138. batch.insert( doc.selection.getFirstPosition(), ' ' );
  139. } );
  140. expect( getData( doc ) ).to.equal( '<blockQuote><paragraph>[]</paragraph></blockQuote>' );
  141. } );
  142. it( 'should not replace greater-than character when inside heading', () => {
  143. setData( doc, '<heading1>>[]</heading1>' );
  144. doc.enqueueChanges( () => {
  145. batch.insert( doc.selection.getFirstPosition(), ' ' );
  146. } );
  147. expect( getData( doc ) ).to.equal( '<heading1>> []</heading1>' );
  148. } );
  149. it( 'should not replace greater-than character when inside numbered list', () => {
  150. setData( doc, '<listItem indent="0" type="numbered">1. >[]</listItem>' );
  151. doc.enqueueChanges( () => {
  152. batch.insert( doc.selection.getFirstPosition(), ' ' );
  153. } );
  154. expect( getData( doc ) ).to.equal( '<listItem indent="0" type="numbered">1. > []</listItem>' );
  155. } );
  156. it( 'should not replace greater-than character when inside buletted list', () => {
  157. setData( doc, '<listItem indent="0" type="bulleted">1. >[]</listItem>' );
  158. doc.enqueueChanges( () => {
  159. batch.insert( doc.selection.getFirstPosition(), ' ' );
  160. } );
  161. expect( getData( doc ) ).to.equal( '<listItem indent="0" type="bulleted">1. > []</listItem>' );
  162. } );
  163. } );
  164. describe( 'Inline autoformat', () => {
  165. it( 'should replace both `**` with bold', () => {
  166. setData( doc, '<paragraph>**foobar*[]</paragraph>' );
  167. doc.enqueueChanges( () => {
  168. batch.insert( doc.selection.getFirstPosition(), '*' );
  169. } );
  170. expect( getData( doc ) ).to.equal( '<paragraph><$text bold="true">foobar</$text>[]</paragraph>' );
  171. } );
  172. it( 'should replace both `*` with italic', () => {
  173. setData( doc, '<paragraph>*foobar[]</paragraph>' );
  174. doc.enqueueChanges( () => {
  175. batch.insert( doc.selection.getFirstPosition(), '*' );
  176. } );
  177. expect( getData( doc ) ).to.equal( '<paragraph><$text italic="true">foobar</$text>[]</paragraph>' );
  178. } );
  179. it( 'nothing should be replaces when typing `*`', () => {
  180. setData( doc, '<paragraph>foobar[]</paragraph>' );
  181. doc.enqueueChanges( () => {
  182. batch.insert( doc.selection.getFirstPosition(), '*' );
  183. } );
  184. expect( getData( doc ) ).to.equal( '<paragraph>foobar*[]</paragraph>' );
  185. } );
  186. it( 'should format inside the text', () => {
  187. setData( doc, '<paragraph>foo **bar*[] baz</paragraph>' );
  188. doc.enqueueChanges( () => {
  189. batch.insert( doc.selection.getFirstPosition(), '*' );
  190. } );
  191. expect( getData( doc ) ).to.equal( '<paragraph>foo <$text bold="true">bar</$text>[] baz</paragraph>' );
  192. } );
  193. } );
  194. describe( 'without commands', () => {
  195. beforeEach( () => {
  196. return VirtualTestEditor
  197. .create( {
  198. plugins: [ Enter, Paragraph, Autoformat ]
  199. } )
  200. .then( newEditor => {
  201. editor = newEditor;
  202. doc = editor.document;
  203. batch = doc.batch();
  204. } );
  205. } );
  206. it( 'should not replace asterisk with bulleted list item', () => {
  207. setData( doc, '<paragraph>*[]</paragraph>' );
  208. doc.enqueueChanges( () => {
  209. batch.insert( doc.selection.getFirstPosition(), ' ' );
  210. } );
  211. expect( getData( doc ) ).to.equal( '<paragraph>* []</paragraph>' );
  212. } );
  213. it( 'should not replace minus character with bulleted list item', () => {
  214. setData( doc, '<paragraph>-[]</paragraph>' );
  215. doc.enqueueChanges( () => {
  216. batch.insert( doc.selection.getFirstPosition(), ' ' );
  217. } );
  218. expect( getData( doc ) ).to.equal( '<paragraph>- []</paragraph>' );
  219. } );
  220. it( 'should not replace digit with numbered list item', () => {
  221. setData( doc, '<paragraph>1.[]</paragraph>' );
  222. doc.enqueueChanges( () => {
  223. batch.insert( doc.selection.getFirstPosition(), ' ' );
  224. } );
  225. expect( getData( doc ) ).to.equal( '<paragraph>1. []</paragraph>' );
  226. } );
  227. it( 'should not replace hash character with heading', () => {
  228. setData( doc, '<paragraph>#[]</paragraph>' );
  229. doc.enqueueChanges( () => {
  230. batch.insert( doc.selection.getFirstPosition(), ' ' );
  231. } );
  232. expect( getData( doc ) ).to.equal( '<paragraph># []</paragraph>' );
  233. } );
  234. it( 'should not replace two hash characters with heading level 2', () => {
  235. setData( doc, '<paragraph>##[]</paragraph>' );
  236. doc.enqueueChanges( () => {
  237. batch.insert( doc.selection.getFirstPosition(), ' ' );
  238. } );
  239. expect( getData( doc ) ).to.equal( '<paragraph>## []</paragraph>' );
  240. } );
  241. it( 'should not replace both `**` with bold', () => {
  242. setData( doc, '<paragraph>**foobar*[]</paragraph>' );
  243. doc.enqueueChanges( () => {
  244. batch.insert( doc.selection.getFirstPosition(), '*' );
  245. } );
  246. expect( getData( doc ) ).to.equal( '<paragraph>**foobar**[]</paragraph>' );
  247. } );
  248. it( 'should not replace both `*` with italic', () => {
  249. setData( doc, '<paragraph>*foobar[]</paragraph>' );
  250. doc.enqueueChanges( () => {
  251. batch.insert( doc.selection.getFirstPosition(), '*' );
  252. } );
  253. expect( getData( doc ) ).to.equal( '<paragraph>*foobar*[]</paragraph>' );
  254. } );
  255. it( 'should not replace `>` with block quote', () => {
  256. setData( doc, '<paragraph>>[]</paragraph>' );
  257. doc.enqueueChanges( () => {
  258. batch.insert( doc.selection.getFirstPosition(), ' ' );
  259. } );
  260. expect( getData( doc ) ).to.equal( '<paragraph>> []</paragraph>' );
  261. } );
  262. it( 'should use only configured headings', () => {
  263. return VirtualTestEditor
  264. .create( {
  265. plugins: [ Enter, Paragraph, Autoformat, ListEngine, HeadingEngine ],
  266. heading: {
  267. options: [
  268. { modelElement: 'paragraph' },
  269. { modelElement: 'heading1', viewElement: 'h2' }
  270. ]
  271. }
  272. } )
  273. .then( editor => {
  274. doc = editor.document;
  275. batch = doc.batch();
  276. setData( doc, '<paragraph>##[]</paragraph>' );
  277. doc.enqueueChanges( () => {
  278. batch.insert( doc.selection.getFirstPosition(), ' ' );
  279. } );
  280. expect( getData( doc ) ).to.equal( '<paragraph>## []</paragraph>' );
  281. } );
  282. } );
  283. } );
  284. } );