/** * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ /* globals document, Event */ import ClassicEditorUI from '../src/classiceditorui'; import ClassicEditorUIView from '../src/classiceditoruiview'; import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor'; import ClassicEditor from '../src/classiceditor'; import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold'; import DataApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/dataapimixin'; import ElementApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/elementapimixin'; import RootElement from '@ckeditor/ckeditor5-engine/src/model/rootelement'; import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; import log from '@ckeditor/ckeditor5-utils/src/log'; import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset'; import { describeMemoryUsage, testMemoryUsage } from '@ckeditor/ckeditor5-core/tests/_utils/memory'; describe( 'ClassicEditor', () => { let editor, editorElement; testUtils.createSinonSandbox(); beforeEach( () => { editorElement = document.createElement( 'div' ); editorElement.innerHTML = '

foo bar

'; document.body.appendChild( editorElement ); testUtils.sinon.stub( log, 'warn' ).callsFake( () => {} ); } ); afterEach( () => { editorElement.remove(); } ); describe( 'constructor()', () => { beforeEach( () => { editor = new ClassicEditor( editorElement ); } ); it( 'uses HTMLDataProcessor', () => { expect( editor.data.processor ).to.be.instanceof( HtmlDataProcessor ); } ); it( 'has a Data Interface', () => { expect( testUtils.isMixed( ClassicEditor, DataApiMixin ) ).to.true; } ); it( 'has a Element Interface', () => { expect( testUtils.isMixed( ClassicEditor, ElementApiMixin ) ).to.true; } ); it( 'creates main root element', () => { expect( editor.model.document.getRoot( 'main' ) ).to.instanceof( RootElement ); } ); it( 'contains the source element as #sourceElement property', () => { expect( editor.sourceElement ).to.equal( editorElement ); } ); it( 'handles form element', () => { const form = document.createElement( 'form' ); const textarea = document.createElement( 'textarea' ); form.appendChild( textarea ); document.body.appendChild( form ); // Prevents page realods in Firefox ;| form.addEventListener( 'submit', evt => { evt.preventDefault(); } ); return ClassicEditor.create( textarea, { plugins: [ Paragraph ] } ).then( editor => { expect( textarea.value ).to.equal( '' ); editor.setData( '

Foo

' ); form.dispatchEvent( new Event( 'submit', { // We need to be able to do preventDefault() to prevent page reloads in Firefox. cancelable: true } ) ); expect( textarea.value ).to.equal( '

Foo

' ); return editor.destroy().then( () => { form.remove(); } ); } ); } ); it( 'allows to pass data to the constructor', () => { return ClassicEditor.create( '

Hello world!

', { plugins: [ Paragraph ] } ).then( editor => { expect( editor.getData() ).to.equal( '

Hello world!

' ); } ); } ); describe( 'ui', () => { it( 'creates the UI using BoxedEditorUI classes', () => { expect( editor.ui ).to.be.instanceof( ClassicEditorUI ); expect( editor.ui.view ).to.be.instanceof( ClassicEditorUIView ); } ); } ); } ); describe( 'create()', () => { beforeEach( () => { return ClassicEditor .create( editorElement, { plugins: [ Paragraph, Bold ] } ) .then( newEditor => { editor = newEditor; } ); } ); afterEach( () => { return editor.destroy(); } ); it( 'creates an instance which inherits from the ClassicEditor', () => { expect( editor ).to.be.instanceof( ClassicEditor ); } ); it( 'loads data from the editor element', () => { expect( editor.getData() ).to.equal( '

foo bar

' ); } ); // #53 it( 'creates an instance of a ClassicEditor child class', () => { class CustomClassicEditor extends ClassicEditor {} return CustomClassicEditor .create( editorElement, { plugins: [ Paragraph, Bold ] } ) .then( newEditor => { expect( newEditor ).to.be.instanceof( CustomClassicEditor ); expect( newEditor ).to.be.instanceof( ClassicEditor ); expect( newEditor.getData() ).to.equal( '

foo bar

' ); return newEditor.destroy(); } ); } ); it( 'should have undefined the #sourceElement if editor was initialized with data', () => { return ClassicEditor .create( '

Foo.

', { plugins: [ Paragraph, Bold ] } ) .then( newEditor => { expect( newEditor.sourceElement ).to.be.undefined; return newEditor.destroy(); } ); } ); describe( 'ui', () => { it( 'inserts editor UI next to editor element', () => { expect( editor.ui.view.element.previousSibling ).to.equal( editorElement ); } ); it( 'attaches editable UI as view\'s DOM root', () => { expect( editor.editing.view.getDomRoot() ).to.equal( editor.ui.view.editable.element ); } ); } ); } ); describe( 'create - events', () => { afterEach( () => { return editor.destroy(); } ); it( 'fires all events in the right order', () => { const fired = []; function spy( evt ) { fired.push( evt.name ); } class EventWatcher extends Plugin { init() { this.editor.on( 'pluginsReady', spy ); this.editor.ui.on( 'ready', spy ); this.editor.on( 'dataReady', spy ); this.editor.on( 'ready', spy ); } } return ClassicEditor .create( editorElement, { plugins: [ EventWatcher ] } ) .then( newEditor => { expect( fired ).to.deep.equal( [ 'pluginsReady', 'ready', 'dataReady', 'ready' ] ); editor = newEditor; } ); } ); it( 'fires dataReady once data is loaded', () => { let data; class EventWatcher extends Plugin { init() { this.editor.on( 'dataReady', () => { data = this.editor.getData(); } ); } } return ClassicEditor .create( editorElement, { plugins: [ EventWatcher, Paragraph, Bold ] } ) .then( newEditor => { expect( data ).to.equal( '

foo bar

' ); editor = newEditor; } ); } ); it( 'fires ready once UI is rendered', () => { let isReady; class EventWatcher extends Plugin { init() { this.editor.ui.on( 'ready', () => { isReady = this.editor.ui.view.isRendered; } ); } } return ClassicEditor .create( editorElement, { plugins: [ EventWatcher ] } ) .then( newEditor => { expect( isReady ).to.be.true; editor = newEditor; } ); } ); } ); describe( 'destroy', () => { beforeEach( function() { return ClassicEditor .create( editorElement, { plugins: [ Paragraph ] } ) .then( newEditor => { editor = newEditor; } ); } ); it( 'sets the data back to the editor element', () => { editor.setData( '

foo

' ); return editor.destroy() .then( () => { expect( editorElement.innerHTML ).to.equal( '

foo

' ); } ); } ); it( 'does not update the source element if editor was initialized with data', () => { return ClassicEditor .create( '

Foo.

', { plugins: [ Paragraph, Bold ] } ) .then( newEditor => { const spy = sinon.stub( newEditor, 'updateSourceElement' ); return newEditor.destroy() .then( () => { expect( spy.called ).to.be.false; spy.restore(); } ); } ); } ); it( 'restores the editor element', () => { expect( editor.sourceElement.style.display ).to.equal( 'none' ); return editor.destroy() .then( () => { expect( editor.sourceElement.style.display ).to.equal( '' ); } ); } ); } ); describeMemoryUsage( () => { testMemoryUsage( 'should not grow on multiple create/destroy', () => ClassicEditor .create( document.querySelector( '#mem-editor' ), { plugins: [ ArticlePluginSet ], toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote' ], image: { toolbar: [ 'imageStyle:full', 'imageStyle:side', '|', 'imageTextAlternative' ] } } ) ); } ); } );