8
0

spellchecking.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /*
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* globals window, document, setTimeout */
  6. import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classic';
  7. import Enter from '@ckeditor/ckeditor5-enter/src/enter';
  8. import Typing from '../src/typing';
  9. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  10. import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
  11. import Undo from '@ckeditor/ckeditor5-undo/src/undo';
  12. import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range';
  13. import ViewSelection from '@ckeditor/ckeditor5-engine/src/view/selection';
  14. import { getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  15. import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
  16. describe( 'Spellchecking integration', () => {
  17. let editor;
  18. before( () => {
  19. const container = document.createElement( 'div' );
  20. document.body.appendChild( container );
  21. return ClassicEditor.create( container, {
  22. plugins: [ Enter, Typing, Paragraph, Bold, Undo ]
  23. } )
  24. .then( newEditor => {
  25. editor = newEditor;
  26. } );
  27. } );
  28. beforeEach( () => {
  29. editor.setData(
  30. '<p>The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</p>' +
  31. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  32. } );
  33. describe( 'Plain text spellchecking (mutations)', () => {
  34. // This tests emulates spellchecker correction on non-styled text by firing proper mutations.
  35. it( 'should replace with longer word', () => {
  36. emulateSpellcheckerMutation( editor, 0, 13,
  37. 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane',
  38. 'The Foo house a is a Foo hous e. A Foo athat and Foo xhat. This is an istane' );
  39. expectContent( editor,
  40. '<paragraph>The Foo house[] a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</paragraph>' +
  41. '<paragraph>Banana, orenge, appfle and the new comppputer</paragraph>',
  42. '<p>The Foo house{} a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</p>' +
  43. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  44. } );
  45. it( 'should replace with shorter word (merging letter after)', () => {
  46. emulateSpellcheckerMutation( editor, 0, 29,
  47. 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane',
  48. 'The Foo hous a is a Foo house. A Foo athat and Foo xhat. This is an istane' );
  49. expectContent( editor,
  50. '<paragraph>The Foo hous a is a Foo house[]. A Foo athat and Foo xhat. This is an istane</paragraph>' +
  51. '<paragraph>Banana, orenge, appfle and the new comppputer</paragraph>',
  52. '<p>The Foo hous a is a Foo house{}. A Foo athat and Foo xhat. This is an istane</p>' +
  53. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  54. } );
  55. it( 'should replace with same length text', () => {
  56. emulateSpellcheckerMutation( editor, 0, 43,
  57. 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane',
  58. 'The Foo hous a is a Foo hous e. A Food that and Foo xhat. This is an istane' );
  59. expectContent( editor,
  60. '<paragraph>The Foo hous a is a Foo hous e. A Food that[] and Foo xhat. This is an istane</paragraph>' +
  61. '<paragraph>Banana, orenge, appfle and the new comppputer</paragraph>',
  62. '<p>The Foo hous a is a Foo hous e. A Food that{} and Foo xhat. This is an istane</p>' +
  63. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  64. } );
  65. it( 'should replace with longer word on the paragraph end', () => {
  66. emulateSpellcheckerMutation( editor, 0, 77,
  67. 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane',
  68. 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an instance' );
  69. expectContent( editor,
  70. '<paragraph>The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an instance[]</paragraph>' +
  71. '<paragraph>Banana, orenge, appfle and the new comppputer</paragraph>',
  72. '<p>The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an instance{}</p>' +
  73. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  74. } );
  75. it( 'should replace with shorter word on the paragraph end', () => {
  76. emulateSpellcheckerMutation( editor, 1, 43,
  77. 'Banana, orenge, appfle and the new comppputer',
  78. 'Banana, orenge, appfle and the new computer' );
  79. expectContent( editor,
  80. '<paragraph>The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</paragraph>' +
  81. '<paragraph>Banana, orenge, appfle and the new computer[]</paragraph>',
  82. '<p>The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</p>' +
  83. '<p>Banana, orenge, appfle and the new computer{}</p>' );
  84. } );
  85. } );
  86. describe( 'Plain text spellchecking (insertText)', () => {
  87. // This tests emulates spellchecker correction on non-styled text by inserting correction text via native 'insertText' command.
  88. it( 'should replace with longer word (collapsed)', ( done ) => {
  89. emulateSpellcheckerInsertText( editor, 0, 12, 12, 'e' );
  90. setTimeout( () => {
  91. expectContent( editor,
  92. '<paragraph>The Foo house[] a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</paragraph>' +
  93. '<paragraph>Banana, orenge, appfle and the new comppputer</paragraph>',
  94. '<p>The Foo house{} a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</p>' +
  95. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  96. done();
  97. }, 0 );
  98. } );
  99. it( 'should replace with longer word (non-collapsed)', ( done ) => {
  100. emulateSpellcheckerInsertText( editor, 0, 8, 12, 'house' );
  101. setTimeout( () => {
  102. expectContent( editor,
  103. '<paragraph>The Foo house[] a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</paragraph>' +
  104. '<paragraph>Banana, orenge, appfle and the new comppputer</paragraph>',
  105. '<p>The Foo house{} a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</p>' +
  106. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  107. done();
  108. }, 0 );
  109. } );
  110. it( 'should replace with shorter word (merging letter after - collapsed)', ( done ) => {
  111. emulateSpellcheckerInsertText( editor, 0, 28, 30, 'e' );
  112. setTimeout( () => {
  113. expectContent( editor,
  114. '<paragraph>The Foo hous a is a Foo house[]. A Foo athat and Foo xhat. This is an istane</paragraph>' +
  115. '<paragraph>Banana, orenge, appfle and the new comppputer</paragraph>',
  116. '<p>The Foo hous a is a Foo house{}. A Foo athat and Foo xhat. This is an istane</p>' +
  117. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  118. done();
  119. }, 0 );
  120. } );
  121. it( 'should replace with shorter word (merging letter after - non-collapsed)', ( done ) => {
  122. emulateSpellcheckerInsertText( editor, 0, 24, 30, 'house' );
  123. setTimeout( () => {
  124. expectContent( editor,
  125. '<paragraph>The Foo hous a is a Foo house[]. A Foo athat and Foo xhat. This is an istane</paragraph>' +
  126. '<paragraph>Banana, orenge, appfle and the new comppputer</paragraph>',
  127. '<p>The Foo hous a is a Foo house{}. A Foo athat and Foo xhat. This is an istane</p>' +
  128. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  129. done();
  130. }, 0 );
  131. } );
  132. it( 'should replace with same length text', ( done ) => {
  133. emulateSpellcheckerInsertText( editor, 0, 37, 43, 'd that' );
  134. setTimeout( () => {
  135. expectContent( editor,
  136. '<paragraph>The Foo hous a is a Foo hous e. A Food that[] and Foo xhat. This is an istane</paragraph>' +
  137. '<paragraph>Banana, orenge, appfle and the new comppputer</paragraph>',
  138. '<p>The Foo hous a is a Foo hous e. A Food that{} and Foo xhat. This is an istane</p>' +
  139. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  140. done();
  141. }, 0 );
  142. } );
  143. it( 'should replace with longer word on the paragraph end', ( done ) => {
  144. emulateSpellcheckerInsertText( editor, 0, 69, 75, 'instance' );
  145. setTimeout( () => {
  146. expectContent( editor,
  147. '<paragraph>The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an instance[]</paragraph>' +
  148. '<paragraph>Banana, orenge, appfle and the new comppputer</paragraph>',
  149. '<p>The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an instance{}</p>' +
  150. '<p>Banana, orenge, appfle and the new comppputer</p>' );
  151. done();
  152. }, 0 );
  153. } );
  154. it( 'should replace with shorter word on the paragraph end', ( done ) => {
  155. emulateSpellcheckerInsertText( editor, 1, 35, 45, 'computer' );
  156. setTimeout( () => {
  157. expectContent( editor,
  158. '<paragraph>The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</paragraph>' +
  159. '<paragraph>Banana, orenge, appfle and the new computer[]</paragraph>',
  160. '<p>The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane</p>' +
  161. '<p>Banana, orenge, appfle and the new computer{}</p>' );
  162. done();
  163. }, 0 );
  164. } );
  165. } );
  166. } );
  167. function emulateSpellcheckerMutation( editor, nodeIndex, resultPositionIndex, oldText, newText ) {
  168. const view = editor.editing.view;
  169. const viewRoot = view.getRoot();
  170. const viewSelection = new ViewSelection();
  171. viewSelection.collapse( viewRoot.getChild( nodeIndex ).getChild( 0 ), resultPositionIndex );
  172. view.fire( 'mutations',
  173. [ {
  174. type: 'text',
  175. oldText: oldText,
  176. newText: newText,
  177. node: viewRoot.getChild( nodeIndex ).getChild( 0 )
  178. } ],
  179. viewSelection
  180. );
  181. }
  182. function emulateSpellcheckerInsertText( editor, nodeIndex, rangeStart, rangeEnd, text ) {
  183. const model = editor.editing.model;
  184. const modelRoot = model.getRoot();
  185. editor.editing.view.focus();
  186. model.enqueueChanges( () => {
  187. model.selection.setRanges( [
  188. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( nodeIndex ), rangeStart, modelRoot.getChild( nodeIndex ), rangeEnd )
  189. ] );
  190. } );
  191. window.document.execCommand( 'insertText', false, text );
  192. }
  193. function expectContent( editor, expectedModel, expectedView ) {
  194. expect( getModelData( editor.editing.model ) ).to.equal( expectedModel );
  195. expect( getViewData( editor.editing.view ) ).to.equal( expectedView );
  196. }