|
|
@@ -4,14 +4,13 @@
|
|
|
*/
|
|
|
|
|
|
import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
|
|
|
-import ContextualBalloon from '../src/contextualballoon';
|
|
|
-import BalloonPanelView from '../src/panel/balloon/balloonpanelview';
|
|
|
-import View from '../src/view';
|
|
|
-import Template from '../src/template';
|
|
|
+import ContextualBalloon from '../../../src/panel/balloon/contextualballoon';
|
|
|
+import BalloonPanelView from '../../../src/panel/balloon/balloonpanelview';
|
|
|
+import View from '../../../src/view';
|
|
|
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
|
|
|
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
|
|
|
|
|
|
-/* global document */
|
|
|
+/* global document, setTimeout */
|
|
|
|
|
|
describe( 'ContextualBalloon', () => {
|
|
|
let editor, editorElement, balloon, viewA, viewB;
|
|
|
@@ -27,12 +26,21 @@ describe( 'ContextualBalloon', () => {
|
|
|
editor = newEditor;
|
|
|
balloon = editor.plugins.get( ContextualBalloon );
|
|
|
|
|
|
- viewA = new ViewA();
|
|
|
- viewB = new ViewB();
|
|
|
-
|
|
|
// We don't need to test BalloonPanelView attachTo and pin methods it's enough to check if was called with proper data.
|
|
|
sinon.stub( balloon.view, 'attachTo', () => {} );
|
|
|
sinon.stub( balloon.view, 'pin', () => {} );
|
|
|
+
|
|
|
+ viewA = new View();
|
|
|
+ viewB = new View();
|
|
|
+
|
|
|
+ // Add viewA to the pane and init viewB.
|
|
|
+ return Promise.all( [
|
|
|
+ balloon.add( {
|
|
|
+ view: viewA,
|
|
|
+ position: { target: 'fake' }
|
|
|
+ } ),
|
|
|
+ viewB.init(),
|
|
|
+ ] );
|
|
|
} );
|
|
|
} );
|
|
|
|
|
|
@@ -62,20 +70,10 @@ describe( 'ContextualBalloon', () => {
|
|
|
|
|
|
describe( 'hasView()', () => {
|
|
|
it( 'should return true when given view is in stack', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: 'fake' }
|
|
|
- } );
|
|
|
-
|
|
|
expect( balloon.hasView( viewA ) ).to.true;
|
|
|
} );
|
|
|
|
|
|
it( 'should return true when given view is in stack but is not visible', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: 'fake' }
|
|
|
- } );
|
|
|
-
|
|
|
balloon.add( {
|
|
|
view: viewB,
|
|
|
position: { target: 'fake' }
|
|
|
@@ -86,17 +84,39 @@ describe( 'ContextualBalloon', () => {
|
|
|
} );
|
|
|
|
|
|
it( 'should return false when given view is not in stack', () => {
|
|
|
- expect( balloon.hasView( viewA ) ).to.false;
|
|
|
+ expect( balloon.hasView( viewB ) ).to.false;
|
|
|
} );
|
|
|
} );
|
|
|
|
|
|
describe( 'add()', () => {
|
|
|
- it( 'should add view to the stack and display in balloon', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
+ it( 'should return promise resolved when view is ready', ( done ) => {
|
|
|
+ const clock = sinon.useFakeTimers();
|
|
|
+
|
|
|
+ const view = {
|
|
|
+ init: () => {
|
|
|
+ return new Promise( ( resolve ) => {
|
|
|
+ setTimeout( () => {
|
|
|
+ resolve();
|
|
|
+ }, 10 );
|
|
|
+ } );
|
|
|
+ },
|
|
|
+ destroy: () => {}
|
|
|
+ };
|
|
|
+
|
|
|
+ const result = balloon.add( {
|
|
|
+ view: view,
|
|
|
position: { target: 'fake' }
|
|
|
} );
|
|
|
|
|
|
+ expect( result ).to.instanceof( Promise );
|
|
|
+
|
|
|
+ result.then( done );
|
|
|
+
|
|
|
+ clock.tick( 11 );
|
|
|
+ clock.restore();
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should add view to the stack and display in balloon attached using given position options', () => {
|
|
|
expect( balloon.view.content.length ).to.equal( 1 );
|
|
|
expect( balloon.view.content.get( 0 ) ).to.deep.equal( viewA );
|
|
|
expect( balloon.view.pin.calledOnce ).to.true;
|
|
|
@@ -104,20 +124,10 @@ describe( 'ContextualBalloon', () => {
|
|
|
} );
|
|
|
|
|
|
it( 'should pin balloon to the target element', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: document.createElement( 'div' ) }
|
|
|
- } );
|
|
|
-
|
|
|
sinon.assert.calledOnce( balloon.view.pin );
|
|
|
} );
|
|
|
|
|
|
it( 'should throw an error when try to add the same view more than once', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: 'fake' }
|
|
|
- } );
|
|
|
-
|
|
|
expect( () => {
|
|
|
balloon.add( {
|
|
|
view: viewA,
|
|
|
@@ -127,11 +137,6 @@ describe( 'ContextualBalloon', () => {
|
|
|
} );
|
|
|
|
|
|
it( 'should add multiple views to he stack and display last one', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: 'fake' }
|
|
|
- } );
|
|
|
-
|
|
|
balloon.add( {
|
|
|
view: viewB,
|
|
|
position: { target: 'fake' }
|
|
|
@@ -141,47 +146,31 @@ describe( 'ContextualBalloon', () => {
|
|
|
expect( balloon.view.content.get( 0 ) ).to.deep.equal( viewB );
|
|
|
} );
|
|
|
|
|
|
- it( 'should add multiple views to the stack and keep balloon in the same position', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: 'fake', foo: 'bar' }
|
|
|
- } );
|
|
|
-
|
|
|
- balloon.add( {
|
|
|
+ it( 'should keep balloon at the same position after adding next view', () => {
|
|
|
+ return balloon.add( {
|
|
|
view: viewB,
|
|
|
- position: { target: 'fake', bar: 'biz' }
|
|
|
- } );
|
|
|
-
|
|
|
- expect( balloon.view.pin.calledTwice ).to.true;
|
|
|
+ position: { target: 'other' }
|
|
|
+ } )
|
|
|
+ .then( () => {
|
|
|
+ expect( balloon.view.pin.calledTwice ).to.true;
|
|
|
|
|
|
- expect( balloon.view.pin.firstCall.args[ 0 ] ).to.deep.equal( {
|
|
|
- target: 'fake',
|
|
|
- foo: 'bar'
|
|
|
- } );
|
|
|
+ expect( balloon.view.pin.firstCall.args[ 0 ] ).to.deep.equal( {
|
|
|
+ target: 'fake'
|
|
|
+ } );
|
|
|
|
|
|
- expect( balloon.view.pin.secondCall.args[ 0 ] ).to.deep.equal( {
|
|
|
- target: 'fake',
|
|
|
- foo: 'bar'
|
|
|
+ expect( balloon.view.pin.secondCall.args[ 0 ] ).to.deep.equal( {
|
|
|
+ target: 'fake'
|
|
|
+ } );
|
|
|
} );
|
|
|
} );
|
|
|
} );
|
|
|
|
|
|
describe( 'visibleView', () => {
|
|
|
it( 'should return data of currently visible view', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: 'fake' }
|
|
|
- } );
|
|
|
-
|
|
|
expect( balloon.visibleView ).to.equal( viewA );
|
|
|
} );
|
|
|
|
|
|
it( 'should return data of currently visible view when there is more than one in the stack', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: 'fake' }
|
|
|
- } );
|
|
|
-
|
|
|
balloon.add( {
|
|
|
view: viewB,
|
|
|
position: { target: 'fake' }
|
|
|
@@ -191,28 +180,26 @@ describe( 'ContextualBalloon', () => {
|
|
|
} );
|
|
|
|
|
|
it( 'should return `null` when the stack is empty', () => {
|
|
|
+ balloon.remove( viewA );
|
|
|
expect( balloon.visibleView ).to.null;
|
|
|
} );
|
|
|
} );
|
|
|
|
|
|
describe( 'remove()', () => {
|
|
|
+ it( 'should return promise', () => {
|
|
|
+ expect( balloon.remove( viewA ) ).to.instanceof( Promise );
|
|
|
+ } );
|
|
|
+
|
|
|
it( 'should remove given view and hide balloon when there is no other view to display', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: 'fake' }
|
|
|
- } );
|
|
|
+ balloon.view.isVisible = true;
|
|
|
|
|
|
balloon.remove( viewA );
|
|
|
|
|
|
expect( balloon.visibleView ).to.null;
|
|
|
+ expect( balloon.view.isVisible ).to.false;
|
|
|
} );
|
|
|
|
|
|
- it( 'should remove given view and set previous in the stack as visible when removed view was visible', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: 'fake' }
|
|
|
- } );
|
|
|
-
|
|
|
+ it( 'should remove given view and set preceding in the stack as visible when removed view was visible', () => {
|
|
|
balloon.add( {
|
|
|
view: viewB,
|
|
|
position: { target: 'fake' }
|
|
|
@@ -223,9 +210,22 @@ describe( 'ContextualBalloon', () => {
|
|
|
expect( balloon.visibleView ).to.equal( viewA );
|
|
|
} );
|
|
|
|
|
|
- it( 'should remove given view from the stack when view is not visible', () => {
|
|
|
+ it( 'should wait for init of preceding view when was is not ready', ( done ) => {
|
|
|
+ const clock = sinon.useFakeTimers();
|
|
|
+
|
|
|
+ const view = {
|
|
|
+ init: () => {
|
|
|
+ return new Promise( ( resolve ) => {
|
|
|
+ setTimeout( () => {
|
|
|
+ resolve();
|
|
|
+ }, 10 );
|
|
|
+ } );
|
|
|
+ },
|
|
|
+ destroy: () => {}
|
|
|
+ };
|
|
|
+
|
|
|
balloon.add( {
|
|
|
- view: viewA,
|
|
|
+ view: view,
|
|
|
position: { target: 'fake' }
|
|
|
} );
|
|
|
|
|
|
@@ -234,6 +234,18 @@ describe( 'ContextualBalloon', () => {
|
|
|
position: { target: 'fake' }
|
|
|
} );
|
|
|
|
|
|
+ balloon.remove( viewB ).then( done );
|
|
|
+
|
|
|
+ clock.tick( 11 );
|
|
|
+ clock.restore();
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should remove given view from the stack when view is not visible', () => {
|
|
|
+ balloon.add( {
|
|
|
+ view: viewB,
|
|
|
+ position: { target: 'fake' }
|
|
|
+ } );
|
|
|
+
|
|
|
balloon.remove( viewA );
|
|
|
|
|
|
expect( balloon.visibleView ).to.equal( viewB );
|
|
|
@@ -241,26 +253,17 @@ describe( 'ContextualBalloon', () => {
|
|
|
|
|
|
it( 'should throw an error when there is no given view in the stack', () => {
|
|
|
expect( () => {
|
|
|
- balloon.remove( viewA );
|
|
|
+ balloon.remove( viewB );
|
|
|
} ).to.throw( CKEditorError, /^contextualballoon-remove-view-not-exist/ );
|
|
|
} );
|
|
|
} );
|
|
|
|
|
|
describe( 'updatePosition()', () => {
|
|
|
it( 'should attach balloon to the target using the same position options as first view in the stack', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: {
|
|
|
- target: 'fake',
|
|
|
- foo: 'bar'
|
|
|
- }
|
|
|
- } );
|
|
|
-
|
|
|
balloon.add( {
|
|
|
view: viewB,
|
|
|
position: {
|
|
|
- target: 'fake',
|
|
|
- bar: 'biz'
|
|
|
+ target: 'other'
|
|
|
}
|
|
|
} );
|
|
|
|
|
|
@@ -270,59 +273,23 @@ describe( 'ContextualBalloon', () => {
|
|
|
|
|
|
expect( balloon.view.attachTo.calledOnce );
|
|
|
expect( balloon.view.attachTo.firstCall.args[ 0 ] ).to.deep.equal( {
|
|
|
- target: 'fake',
|
|
|
- foo: 'bar'
|
|
|
- } );
|
|
|
- } );
|
|
|
-
|
|
|
- it( 'should remove given view from the stack when view is not visible', () => {
|
|
|
- balloon.add( {
|
|
|
- view: viewA,
|
|
|
- position: { target: 'fake' }
|
|
|
- } );
|
|
|
-
|
|
|
- balloon.add( {
|
|
|
- view: viewB,
|
|
|
- position: { target: 'fake' }
|
|
|
+ target: 'fake'
|
|
|
} );
|
|
|
-
|
|
|
- balloon.remove( viewA );
|
|
|
-
|
|
|
- expect( balloon.visibleView ).to.equal( viewB );
|
|
|
} );
|
|
|
|
|
|
it( 'should throw an error when there is no given view in the stack', () => {
|
|
|
expect( () => {
|
|
|
- balloon.remove( viewA );
|
|
|
+ balloon.remove( viewB );
|
|
|
} ).to.throw( CKEditorError, /^contextualballoon-remove-view-not-exist/ );
|
|
|
} );
|
|
|
} );
|
|
|
|
|
|
describe( 'destroy()', () => {
|
|
|
- it( 'should balloon panel remove view from editor body collection', () => {
|
|
|
+ it( 'should remove balloon panel view from editor body collection and clear stack', () => {
|
|
|
balloon.destroy();
|
|
|
|
|
|
expect( editor.ui.view.body.getIndex( balloon.view ) ).to.equal( -1 );
|
|
|
+ expect( balloon.visibleView ).to.null;
|
|
|
} );
|
|
|
} );
|
|
|
} );
|
|
|
-
|
|
|
-class ViewA extends View {
|
|
|
- constructor( locale ) {
|
|
|
- super( locale );
|
|
|
-
|
|
|
- this.template = new Template( {
|
|
|
- tag: 'div'
|
|
|
- } );
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-class ViewB extends View {
|
|
|
- constructor( locale ) {
|
|
|
- super( locale );
|
|
|
-
|
|
|
- this.template = new Template( {
|
|
|
- tag: 'div'
|
|
|
- } );
|
|
|
- }
|
|
|
-}
|