8
0

entercommand.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. import Editor from '/ckeditor5/editor.js';
  7. import Document from '/ckeditor5/engine/treemodel/document.js';
  8. import { default as EnterCommand, enterBlock } from '/ckeditor5/enter/entercommand.js';
  9. let editor, doc;
  10. beforeEach( () => {
  11. editor = new Editor();
  12. doc = editor.document = new Document();
  13. doc.createRoot( 'main', '$root' );
  14. const command = new EnterCommand( editor );
  15. editor.commands.set( 'enter', command );
  16. const 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. } );
  23. describe( 'EnterCommand', () => {
  24. it( 'enters a block using enqueueChanges', () => {
  25. const spy = sinon.spy( doc, 'enqueueChanges' );
  26. bender.model.setData( doc, '<p>foo<selection /></p>' );
  27. editor.execute( 'enter' );
  28. expect( bender.model.getData( doc, { withoutSelection: true } ) ).to.equal( '<p>foo</p><p></p>' );
  29. expect( spy.calledOnce ).to.be.true;
  30. } );
  31. } );
  32. describe( 'enterBlock', () => {
  33. describe( 'collapsed selection', () => {
  34. test(
  35. 'does nothing in the root',
  36. 'foo<selection />bar',
  37. 'foo<selection />bar',
  38. { defaultBlockName: 'p' }
  39. );
  40. test(
  41. 'splits block',
  42. '<p>x</p><p>foo<selection />bar</p><p>y</p>',
  43. '<p>x</p><p>foo</p><p><selection />bar</p><p>y</p>',
  44. { defaultBlockName: 'p' }
  45. );
  46. test(
  47. 'splits block (other than default)',
  48. '<p>x</p><h>foo<selection />bar</h><p>y</p>',
  49. '<p>x</p><h>foo</h><h><selection />bar</h><p>y</p>',
  50. { defaultBlockName: 'p' }
  51. );
  52. test(
  53. 'splits block at the end',
  54. '<p>x</p><p>foo<selection /></p><p>y</p>',
  55. '<p>x</p><p>foo</p><p><selection /></p><p>y</p>',
  56. { defaultBlockName: 'p' }
  57. );
  58. test(
  59. 'splits block at the beginning',
  60. '<p>x</p><p><selection />foo</p><p>y</p>',
  61. '<p>x</p><p></p><p><selection />foo</p><p>y</p>',
  62. { defaultBlockName: 'p' }
  63. );
  64. test(
  65. 'splits block at the beginning (other than default)',
  66. '<p>x</p><h><selection />foo</h><p>y</p>',
  67. '<p>x</p><h></h><h><selection />foo</h><p>y</p>',
  68. { defaultBlockName: 'p' }
  69. );
  70. test(
  71. 'creates default block when leaving other block',
  72. '<h>foo<selection /></h><p>x</p>',
  73. '<h>foo</h><p><selection /></p><p>x</p>',
  74. { defaultBlockName: 'p' }
  75. );
  76. test(
  77. 'does not rename when default block is not allowed',
  78. '<h>foo<selection /></h><p>x</p>',
  79. '<h>foo</h><h><selection /></h><p>x</p>',
  80. { defaultBlockName: 'xxx' }
  81. );
  82. test(
  83. 'inserts new block after empty one',
  84. '<p>x</p><p><selection /></p><p>y</p>',
  85. '<p>x</p><p></p><p><selection /></p><p>y</p>',
  86. { defaultBlockName: 'p' }
  87. );
  88. test(
  89. 'inserts new block after empty one (other than default)',
  90. '<p>x</p><h><selection /></h><p>y</p>',
  91. '<p>x</p><h></h><p><selection /></p><p>y</p>',
  92. { defaultBlockName: 'p' }
  93. );
  94. } );
  95. describe( 'non-collapsed selection', () => {
  96. test(
  97. 'only deletes the content when directly in the root',
  98. 'fo<selection>ob</selection>ar',
  99. 'fo<selection />ar',
  100. { defaultBlockName: 'p' }
  101. );
  102. test(
  103. 'deletes text and splits',
  104. '<p>ab<selection>cd</selection>ef</p><p>ghi</p>',
  105. '<p>ab</p><p><selection />ef</p><p>ghi</p>',
  106. { defaultBlockName: 'p' }
  107. );
  108. test(
  109. 'deletes text and splits (other than default)',
  110. '<h>ab<selection>cd</selection>ef</h>',
  111. '<h>ab</h><h><selection />ef</h>',
  112. { defaultBlockName: 'p' }
  113. );
  114. test(
  115. 'places selection in the 2nd element',
  116. '<h>ab<selection>c</h><p>d</selection>ef</p><p>ghi</p>',
  117. '<h>ab</h><p><selection />ef</p><p>ghi</p>',
  118. { defaultBlockName: 'p' }
  119. );
  120. test(
  121. 'leaves one empty element after one was fully selected',
  122. '<p>x</p><p><selection>abcdef</selection></p><p>y</p>',
  123. '<p>x</p><p><selection /></p><p>y</p>',
  124. { defaultBlockName: 'p' }
  125. );
  126. test(
  127. 'leaves one (default) empty element after one was fully selected',
  128. '<h><selection>abcdef</selection></h>',
  129. '<p><selection /></p>',
  130. { defaultBlockName: 'p' }
  131. );
  132. test(
  133. 'leaves one (default) empty element after two were fully selected',
  134. '<h><selection>abc</h><p>def</selection></p>',
  135. '<p><selection /></p>',
  136. { defaultBlockName: 'p' }
  137. );
  138. test(
  139. 'leaves one (default) empty element after two were fully selected (backward)',
  140. '<h><selection backward>abc</h><p>def</selection></p>',
  141. '<p><selection /></p>',
  142. { defaultBlockName: 'p' }
  143. );
  144. it( 'uses composer.deleteContents', () => {
  145. const spy = sinon.spy();
  146. doc.composer.on( 'deleteContents', spy );
  147. bender.model.setData( doc, '<p><selection>x</selection></p>' );
  148. enterBlock( doc.batch(), doc.selection, { defaultBlockName: 'p' } );
  149. expect( spy.calledOnce ).to.be.true;
  150. } );
  151. } );
  152. function test( title, input, output, options ) {
  153. it( title, () => {
  154. bender.model.setData( doc, input );
  155. enterBlock( doc.batch(), doc.selection, options );
  156. expect( bender.model.getData( doc ) ).to.equal( output );
  157. } );
  158. }
  159. } );