/* * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ /* globals window, document, setTimeout */ import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'; import Enter from '@ckeditor/ckeditor5-enter/src/enter'; import Typing from '../src/typing'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold'; import Undo from '@ckeditor/ckeditor5-undo/src/undo'; import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range'; import ViewSelection from '@ckeditor/ckeditor5-engine/src/view/selection'; import { getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model'; import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view'; describe( 'Typing – spellchecking integration', () => { let editor, onChangesDone, container; before( () => { container = document.createElement( 'div' ); document.body.appendChild( container ); return ClassicEditor .create( container, { plugins: [ Enter, Typing, Paragraph, Bold, Undo ] } ) .then( newEditor => { editor = newEditor; } ); } ); after( () => { container.remove(); return editor.destroy(); } ); beforeEach( () => { if ( onChangesDone ) { editor.model.document.off( 'change', onChangesDone ); onChangesDone = null; } editor.setData( '

The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new comppputer

' ); } ); describe( 'Plain text spellchecking (mutations)', () => { // This tests emulates spellchecker correction on non-styled text by firing proper mutations. it( 'should replace with longer word', () => { emulateSpellcheckerMutation( editor, 0, 13, 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane', 'The Foo house a is a Foo hous e. A Foo athat and Foo xhat. This is an istane' ); expectContent( editor, 'The Foo house[] a is a Foo hous e. A Foo athat and Foo xhat. This is an istane' + 'Banana, orenge, appfle and the new comppputer', '

The Foo house{} a is a Foo hous e. A Foo athat and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new comppputer

' ); } ); it( 'should replace with shorter word (merging letter after)', () => { emulateSpellcheckerMutation( editor, 0, 29, 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane', 'The Foo hous a is a Foo house. A Foo athat and Foo xhat. This is an istane' ); expectContent( editor, 'The Foo hous a is a Foo house[]. A Foo athat and Foo xhat. This is an istane' + 'Banana, orenge, appfle and the new comppputer', '

The Foo hous a is a Foo house{}. A Foo athat and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new comppputer

' ); } ); it( 'should replace with same length text', () => { emulateSpellcheckerMutation( editor, 0, 43, 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane', 'The Foo hous a is a Foo hous e. A Food that and Foo xhat. This is an istane' ); expectContent( editor, 'The Foo hous a is a Foo hous e. A Food that[] and Foo xhat. This is an istane' + 'Banana, orenge, appfle and the new comppputer', '

The Foo hous a is a Foo hous e. A Food that{} and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new comppputer

' ); } ); it( 'should replace with longer word on the paragraph end', () => { emulateSpellcheckerMutation( editor, 0, 77, 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane', 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an instance' ); expectContent( editor, 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an instance[]' + 'Banana, orenge, appfle and the new comppputer', '

The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an instance{}

' + '

Banana, orenge, appfle and the new comppputer

' ); } ); it( 'should replace with shorter word on the paragraph end', () => { emulateSpellcheckerMutation( editor, 1, 43, 'Banana, orenge, appfle and the new comppputer', 'Banana, orenge, appfle and the new computer' ); expectContent( editor, 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane' + 'Banana, orenge, appfle and the new computer[]', '

The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new computer{}

' ); } ); } ); describe( 'Plain text spellchecking (insertText)', () => { // This tests emulates spellchecker correction on non-styled text by inserting correction text via native 'insertText' command. it( 'should replace with longer word (collapsed)', done => { onChangesDone = () => { expectContent( editor, 'The Foo house[] a is a Foo hous e. A Foo athat and Foo xhat. This is an istane' + 'Banana, orenge, appfle and the new comppputer', '

The Foo house{} a is a Foo hous e. A Foo athat and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new comppputer

' ); done(); }; emulateSpellcheckerInsertText( editor, 0, 12, 12, 'e', onChangesDone ); } ); it( 'should replace with longer word (non-collapsed)', done => { onChangesDone = () => { expectContent( editor, 'The Foo house[] a is a Foo hous e. A Foo athat and Foo xhat. This is an istane' + 'Banana, orenge, appfle and the new comppputer', '

The Foo house{} a is a Foo hous e. A Foo athat and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new comppputer

' ); done(); }; emulateSpellcheckerInsertText( editor, 0, 8, 12, 'house', onChangesDone ); } ); it( 'should replace with shorter word (merging letter after - collapsed)', done => { onChangesDone = () => { expectContent( editor, 'The Foo hous a is a Foo house[]. A Foo athat and Foo xhat. This is an istane' + 'Banana, orenge, appfle and the new comppputer', '

The Foo hous a is a Foo house{}. A Foo athat and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new comppputer

' ); done(); }; emulateSpellcheckerInsertText( editor, 0, 28, 30, 'e', onChangesDone ); } ); it( 'should replace with shorter word (merging letter after - non-collapsed)', done => { onChangesDone = () => { expectContent( editor, 'The Foo hous a is a Foo house[]. A Foo athat and Foo xhat. This is an istane' + 'Banana, orenge, appfle and the new comppputer', '

The Foo hous a is a Foo house{}. A Foo athat and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new comppputer

' ); done(); }; emulateSpellcheckerInsertText( editor, 0, 24, 30, 'house', onChangesDone ); } ); it( 'should replace with same length text', done => { onChangesDone = () => { expectContent( editor, 'The Foo hous a is a Foo hous e. A Food that[] and Foo xhat. This is an istane' + 'Banana, orenge, appfle and the new comppputer', '

The Foo hous a is a Foo hous e. A Food that{} and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new comppputer

' ); done(); }; emulateSpellcheckerInsertText( editor, 0, 37, 43, 'd that', onChangesDone ); } ); it( 'should replace with longer word on the paragraph end', done => { onChangesDone = () => { expectContent( editor, 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an instance[]' + 'Banana, orenge, appfle and the new comppputer', '

The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an instance{}

' + '

Banana, orenge, appfle and the new comppputer

' ); done(); }; emulateSpellcheckerInsertText( editor, 0, 69, 75, 'instance', onChangesDone ); } ); it( 'should replace with shorter word on the paragraph end', done => { onChangesDone = () => { expectContent( editor, 'The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane' + 'Banana, orenge, appfle and the new computer[]', '

The Foo hous a is a Foo hous e. A Foo athat and Foo xhat. This is an istane

' + '

Banana, orenge, appfle and the new computer{}

' ); done(); }; emulateSpellcheckerInsertText( editor, 1, 35, 45, 'computer', onChangesDone ); } ); } ); } ); function emulateSpellcheckerMutation( editor, nodeIndex, resultPositionIndex, oldText, newText ) { const view = editor.editing.view; const viewRoot = view.document.getRoot(); const viewSelection = new ViewSelection(); viewSelection._setTo( viewRoot.getChild( nodeIndex ).getChild( 0 ), resultPositionIndex ); view.document.fire( 'mutations', [ { type: 'text', oldText, newText, node: viewRoot.getChild( nodeIndex ).getChild( 0 ) } ], viewSelection ); } function emulateSpellcheckerInsertText( editor, nodeIndex, rangeStart, rangeEnd, text, onChangesDoneCallback ) { const model = editor.model; const modelRoot = model.document.getRoot(); window.focus(); editor.editing.view.focus(); model.change( writer => { writer.setSelection( ModelRange.createFromParentsAndOffsets( modelRoot.getChild( nodeIndex ), rangeStart, modelRoot.getChild( nodeIndex ), rangeEnd ) ); } ); model.document.once( 'change', () => { setTimeout( onChangesDoneCallback, 100 ); }, { priority: 'low' } ); window.document.execCommand( 'insertText', false, text ); } function expectContent( editor, expectedModel, expectedView ) { expect( getModelData( editor.model ) ).to.equal( expectedModel ); expect( getViewData( editor.editing.view ) ).to.equal( expectedView ); }