8
0

deleteobserver.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* globals document */
  6. import DeleteObserver from '../src/deleteobserver';
  7. import ViewDocument from '@ckeditor/ckeditor5-engine/src/view/document';
  8. import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
  9. import createViewRoot from '@ckeditor/ckeditor5-engine/tests/view/_utils/createroot';
  10. import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';
  11. import env from '@ckeditor/ckeditor5-utils/src/env';
  12. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  13. testUtils.createSinonSandbox();
  14. describe( 'DeleteObserver', () => {
  15. let viewDocument;
  16. beforeEach( () => {
  17. viewDocument = new ViewDocument();
  18. viewDocument.addObserver( DeleteObserver );
  19. } );
  20. // See ckeditor/ckeditor5-enter#10.
  21. it( 'can be initialized', () => {
  22. expect( () => {
  23. createViewRoot( viewDocument );
  24. viewDocument.attachDomRoot( document.createElement( 'div' ) );
  25. } ).to.not.throw();
  26. } );
  27. describe( 'delete event', () => {
  28. it( 'is fired on keydown', () => {
  29. const spy = sinon.spy();
  30. viewDocument.on( 'delete', spy );
  31. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  32. keyCode: getCode( 'delete' )
  33. } ) );
  34. expect( spy.calledOnce ).to.be.true;
  35. const data = spy.args[ 0 ][ 1 ];
  36. expect( data ).to.have.property( 'direction', 'forward' );
  37. expect( data ).to.have.property( 'unit', 'character' );
  38. expect( data ).to.have.property( 'sequence', 1 );
  39. } );
  40. it( 'is fired with a proper direction and unit (on Mac)', () => {
  41. const spy = sinon.spy();
  42. testUtils.sinon.stub( env, 'isMac' ).value( true );
  43. viewDocument.on( 'delete', spy );
  44. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  45. keyCode: getCode( 'backspace' ),
  46. altKey: true
  47. } ) );
  48. expect( spy.calledOnce ).to.be.true;
  49. const data = spy.args[ 0 ][ 1 ];
  50. expect( data ).to.have.property( 'direction', 'backward' );
  51. expect( data ).to.have.property( 'unit', 'word' );
  52. expect( data ).to.have.property( 'sequence', 1 );
  53. } );
  54. it( 'is fired with a proper direction and unit (on non-Mac)', () => {
  55. const spy = sinon.spy();
  56. testUtils.sinon.stub( env, 'isMac' ).value( false );
  57. viewDocument.on( 'delete', spy );
  58. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  59. keyCode: getCode( 'backspace' ),
  60. ctrlKey: true
  61. } ) );
  62. expect( spy.calledOnce ).to.be.true;
  63. const data = spy.args[ 0 ][ 1 ];
  64. expect( data ).to.have.property( 'direction', 'backward' );
  65. expect( data ).to.have.property( 'unit', 'word' );
  66. expect( data ).to.have.property( 'sequence', 1 );
  67. } );
  68. it( 'is not fired on keydown when keyCode does not match backspace or delete', () => {
  69. const spy = sinon.spy();
  70. viewDocument.on( 'delete', spy );
  71. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  72. keyCode: 1
  73. } ) );
  74. expect( spy.calledOnce ).to.be.false;
  75. } );
  76. it( 'is fired with a proper sequence number', () => {
  77. const spy = sinon.spy();
  78. viewDocument.on( 'delete', spy );
  79. // Simulate that a user keeps the "Delete" key.
  80. for ( let i = 0; i < 5; ++i ) {
  81. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  82. keyCode: getCode( 'delete' )
  83. } ) );
  84. }
  85. expect( spy.callCount ).to.equal( 5 );
  86. expect( spy.args[ 0 ][ 1 ] ).to.have.property( 'sequence', 1 );
  87. expect( spy.args[ 1 ][ 1 ] ).to.have.property( 'sequence', 2 );
  88. expect( spy.args[ 2 ][ 1 ] ).to.have.property( 'sequence', 3 );
  89. expect( spy.args[ 3 ][ 1 ] ).to.have.property( 'sequence', 4 );
  90. expect( spy.args[ 4 ][ 1 ] ).to.have.property( 'sequence', 5 );
  91. } );
  92. it( 'clears the sequence when the key was released', () => {
  93. const spy = sinon.spy();
  94. viewDocument.on( 'delete', spy );
  95. // Simulate that a user keeps the "Delete" key.
  96. for ( let i = 0; i < 3; ++i ) {
  97. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  98. keyCode: getCode( 'delete' )
  99. } ) );
  100. }
  101. // Then the user has released the key.
  102. viewDocument.fire( 'keyup', new DomEventData( viewDocument, getDomEvent(), {
  103. keyCode: getCode( 'delete' )
  104. } ) );
  105. // And pressed it once again.
  106. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  107. keyCode: getCode( 'delete' )
  108. } ) );
  109. expect( spy.callCount ).to.equal( 4 );
  110. expect( spy.args[ 0 ][ 1 ] ).to.have.property( 'sequence', 1 );
  111. expect( spy.args[ 1 ][ 1 ] ).to.have.property( 'sequence', 2 );
  112. expect( spy.args[ 2 ][ 1 ] ).to.have.property( 'sequence', 3 );
  113. expect( spy.args[ 3 ][ 1 ] ).to.have.property( 'sequence', 1 );
  114. } );
  115. it( 'works fine with Backspace key', () => {
  116. const spy = sinon.spy();
  117. viewDocument.on( 'delete', spy );
  118. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  119. keyCode: getCode( 'backspace' )
  120. } ) );
  121. viewDocument.fire( 'keyup', new DomEventData( viewDocument, getDomEvent(), {
  122. keyCode: getCode( 'backspace' )
  123. } ) );
  124. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  125. keyCode: getCode( 'backspace' )
  126. } ) );
  127. expect( spy.callCount ).to.equal( 2 );
  128. expect( spy.args[ 0 ][ 1 ] ).to.have.property( 'sequence', 1 );
  129. expect( spy.args[ 1 ][ 1 ] ).to.have.property( 'sequence', 1 );
  130. } );
  131. it( 'does not reset the sequence if other than Backspace or Delete key was released', () => {
  132. const spy = sinon.spy();
  133. viewDocument.on( 'delete', spy );
  134. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  135. keyCode: getCode( 'delete' )
  136. } ) );
  137. viewDocument.fire( 'keyup', new DomEventData( viewDocument, getDomEvent(), {
  138. keyCode: getCode( 'A' )
  139. } ) );
  140. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  141. keyCode: getCode( 'delete' )
  142. } ) );
  143. expect( spy.args[ 0 ][ 1 ] ).to.have.property( 'sequence', 1 );
  144. expect( spy.args[ 1 ][ 1 ] ).to.have.property( 'sequence', 2 );
  145. } );
  146. it( 'should stop keydown event when delete event is stopped', () => {
  147. const keydownSpy = sinon.spy();
  148. viewDocument.on( 'keydown', keydownSpy );
  149. viewDocument.on( 'delete', evt => evt.stop() );
  150. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  151. keyCode: getCode( 'delete' )
  152. } ) );
  153. sinon.assert.notCalled( keydownSpy );
  154. } );
  155. it( 'should not stop keydown event when delete event is not stopped', () => {
  156. const keydownSpy = sinon.spy();
  157. viewDocument.on( 'keydown', keydownSpy );
  158. viewDocument.on( 'delete', evt => evt.stop() );
  159. viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
  160. keyCode: getCode( 'x' )
  161. } ) );
  162. sinon.assert.calledOnce( keydownSpy );
  163. } );
  164. } );
  165. function getDomEvent() {
  166. return {
  167. preventDefault: sinon.spy()
  168. };
  169. }
  170. } );