8
0

entercommand.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import ModelTestEditor from '/tests/core/_utils/modeltesteditor.js';
  6. import { default as EnterCommand, enterBlock } from '/ckeditor5/enter/entercommand.js';
  7. import { getData, setData } from '/tests/engine/_utils/model.js';
  8. let editor, doc, schema;
  9. beforeEach( () => {
  10. return ModelTestEditor.create()
  11. .then( newEditor => {
  12. editor = newEditor;
  13. doc = editor.document;
  14. const command = new EnterCommand( editor );
  15. editor.commands.set( 'enter', command );
  16. schema = doc.schema;
  17. // Note: We could use real names like 'paragraph', but that would make test patterns too long.
  18. // Plus, this is actually a good test that the algorithm can be used for any model.
  19. schema.registerItem( 'img', '$inline' );
  20. schema.registerItem( 'p', '$block' );
  21. schema.registerItem( 'h', '$block' );
  22. schema.allow( { name: '$text', inside: '$root' } );
  23. } );
  24. } );
  25. describe( 'EnterCommand', () => {
  26. it( 'enters a block using enqueueChanges', () => {
  27. setData( doc, '<p>foo[]</p>' );
  28. const spy = sinon.spy( doc, 'enqueueChanges' );
  29. editor.execute( 'enter' );
  30. expect( getData( doc, { withoutSelection: true } ) ).to.equal( '<p>foo</p><p></p>' );
  31. expect( spy.calledOnce ).to.be.true;
  32. } );
  33. it( 'uses paragraph as default block', () => {
  34. schema.registerItem( 'paragraph', '$block' );
  35. setData( doc, '<h>foo[]</h>' );
  36. editor.execute( 'enter' );
  37. expect( getData( doc, { withoutSelection: true } ) ).to.equal( '<h>foo</h><paragraph></paragraph>' );
  38. } );
  39. } );
  40. describe( 'enterBlock', () => {
  41. describe( 'collapsed selection', () => {
  42. test(
  43. 'does nothing in the root',
  44. 'foo[]bar',
  45. 'foo[]bar',
  46. { defaultBlockName: 'p' }
  47. );
  48. test(
  49. 'splits block',
  50. '<p>x</p><p>foo[]bar</p><p>y</p>',
  51. '<p>x</p><p>foo</p><p>[]bar</p><p>y</p>',
  52. { defaultBlockName: 'p' }
  53. );
  54. test(
  55. 'splits block (other than default)',
  56. '<p>x</p><h>foo[]bar</h><p>y</p>',
  57. '<p>x</p><h>foo</h><h>[]bar</h><p>y</p>',
  58. { defaultBlockName: 'p' }
  59. );
  60. test(
  61. 'splits block at the end',
  62. '<p>x</p><p>foo[]</p><p>y</p>',
  63. '<p>x</p><p>foo</p><p>[]</p><p>y</p>',
  64. { defaultBlockName: 'p' }
  65. );
  66. test(
  67. 'splits block at the beginning',
  68. '<p>x</p><p>[]foo</p><p>y</p>',
  69. '<p>x</p><p></p><p>[]foo</p><p>y</p>',
  70. { defaultBlockName: 'p' }
  71. );
  72. test(
  73. 'splits block at the beginning (other than default)',
  74. '<p>x</p><h>[]foo</h><p>y</p>',
  75. '<p>x</p><h></h><h>[]foo</h><p>y</p>',
  76. { defaultBlockName: 'p' }
  77. );
  78. test(
  79. 'creates default block when leaving other block',
  80. '<h>foo[]</h><p>x</p>',
  81. '<h>foo</h><p>[]</p><p>x</p>',
  82. { defaultBlockName: 'p' }
  83. );
  84. test(
  85. 'does not rename when default block is not allowed',
  86. '<h>foo[]</h><p>x</p>',
  87. '<h>foo</h><h>[]</h><p>x</p>',
  88. { defaultBlockName: 'xxx' }
  89. );
  90. test(
  91. 'inserts new block after empty one',
  92. '<p>x</p><p>[]</p><p>y</p>',
  93. '<p>x</p><p></p><p>[]</p><p>y</p>',
  94. { defaultBlockName: 'p' }
  95. );
  96. test(
  97. 'inserts new block after empty one (other than default)',
  98. '<p>x</p><h>[]</h><p>y</p>',
  99. '<p>x</p><h></h><p>[]</p><p>y</p>',
  100. { defaultBlockName: 'p' }
  101. );
  102. } );
  103. describe( 'non-collapsed selection', () => {
  104. test(
  105. 'only deletes the content when directly in the root',
  106. 'fo[ob]ar',
  107. 'fo[]ar',
  108. { defaultBlockName: 'p' }
  109. );
  110. test(
  111. 'deletes text and splits',
  112. '<p>ab[cd]ef</p><p>ghi</p>',
  113. '<p>ab</p><p>[]ef</p><p>ghi</p>',
  114. { defaultBlockName: 'p' }
  115. );
  116. test(
  117. 'deletes text and splits (other than default)',
  118. '<h>ab[cd]ef</h>',
  119. '<h>ab</h><h>[]ef</h>',
  120. { defaultBlockName: 'p' }
  121. );
  122. test(
  123. 'places selection in the 2nd element',
  124. '<h>ab[c</h><p>d]ef</p><p>ghi</p>',
  125. '<h>ab</h><p>[]ef</p><p>ghi</p>',
  126. { defaultBlockName: 'p' }
  127. );
  128. test(
  129. 'leaves one empty element after one was fully selected',
  130. '<p>x</p><p>[abcdef]</p><p>y</p>',
  131. '<p>x</p><p>[]</p><p>y</p>',
  132. { defaultBlockName: 'p' }
  133. );
  134. test(
  135. 'leaves one (default) empty element after one was fully selected',
  136. '<h>[abcdef]</h>',
  137. '<p>[]</p>',
  138. { defaultBlockName: 'p' }
  139. );
  140. test(
  141. 'leaves one (default) empty element after two were fully selected',
  142. '<h>[abc</h><p>def]</p>',
  143. '<p>[]</p>',
  144. { defaultBlockName: 'p' }
  145. );
  146. it( 'leaves one (default) empty element after two were fully selected (backward)', () => {
  147. setData( doc, '<h>[abc</h><p>def]</p>' );
  148. // @TODO: Add option for setting selection direction to model utils.
  149. doc.selection._lastRangeBackward = true;
  150. enterBlock( doc.batch(), doc.selection, { defaultBlockName: 'p' } );
  151. expect( getData( doc ) ).to.equal( '<p>[]</p>' );
  152. } );
  153. it( 'uses composer.deleteContents', () => {
  154. const spy = sinon.spy();
  155. doc.composer.on( 'deleteContents', spy );
  156. setData( doc, '<p>[x]</p>' );
  157. enterBlock( doc.batch(), doc.selection, { defaultBlockName: 'p' } );
  158. expect( spy.calledOnce ).to.be.true;
  159. } );
  160. } );
  161. function test( title, input, output, options ) {
  162. it( title, () => {
  163. setData( doc, input );
  164. enterBlock( doc.batch(), doc.selection, options );
  165. expect( getData( doc ) ).to.equal( output );
  166. } );
  167. }
  168. } );