|
|
@@ -10,6 +10,7 @@ import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
|
|
|
import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
|
|
|
import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
|
|
|
|
|
|
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
|
|
|
import Link from '../src/link';
|
|
|
import LinkEngine from '../src/linkengine';
|
|
|
import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
|
|
|
@@ -28,7 +29,7 @@ describe( 'Link', () => {
|
|
|
document.body.appendChild( editorElement );
|
|
|
|
|
|
return ClassicTestEditor.create( editorElement, {
|
|
|
- plugins: [ Link ]
|
|
|
+ plugins: [ Link, Paragraph ]
|
|
|
} )
|
|
|
.then( newEditor => {
|
|
|
newEditor.editing.view.attachDomRoot( editorElement );
|
|
|
@@ -69,81 +70,211 @@ describe( 'Link', () => {
|
|
|
expect( editor.editing.view.getObserver( ClickObserver ) ).to.instanceOf( ClickObserver );
|
|
|
} );
|
|
|
|
|
|
- describe( 'link toolbar button', () => {
|
|
|
- it( 'should register link button', () => {
|
|
|
- expect( linkButton ).to.instanceOf( ButtonView );
|
|
|
+ describe( 'showPanel()', () => {
|
|
|
+ let balloonAddSpy;
|
|
|
+
|
|
|
+ beforeEach( () => {
|
|
|
+ balloonAddSpy = testUtils.sinon.spy( balloon, 'add' );
|
|
|
+ editor.editing.view.isFocused = true;
|
|
|
} );
|
|
|
|
|
|
- it( 'should bind linkButtonView to link command', () => {
|
|
|
- const command = editor.commands.get( 'link' );
|
|
|
+ it( 'should return promise', () => {
|
|
|
+ // @TODO: test resolved promise.
|
|
|
+ expect( linkFeature.showPanel() ).to.instanceof( Promise );
|
|
|
+ } );
|
|
|
|
|
|
- command.isEnabled = true;
|
|
|
- expect( linkButton.isEnabled ).to.be.true;
|
|
|
+ it( 'should add `formView` to the `ContextualBalloon` and attach panel to the selection when text fragment is selected', () => {
|
|
|
+ setModelData( editor.document, '<paragraph>f[o]o</paragraph>' );
|
|
|
+ const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
|
|
|
|
|
|
- command.isEnabled = false;
|
|
|
- expect( linkButton.isEnabled ).to.be.false;
|
|
|
+ return linkFeature.showPanel()
|
|
|
+ .then( () => {
|
|
|
+ expect( balloon.visibleView ).to.equal( formView );
|
|
|
+ sinon.assert.calledWithExactly( balloonAddSpy, {
|
|
|
+ view: formView,
|
|
|
+ position: {
|
|
|
+ target: selectedRange,
|
|
|
+ limiter: editorElement
|
|
|
+ }
|
|
|
+ } );
|
|
|
+ } );
|
|
|
} );
|
|
|
|
|
|
- it( 'should add link form to the ContextualBalloon on execute event', () => {
|
|
|
- linkButton.fire( 'execute' );
|
|
|
+ it( 'should add `formView` to the `ContextualBalloon` and attach panel to the selection when selection is collapsed', () => {
|
|
|
+ setModelData( editor.document, '<paragraph>f[]oo</paragraph>' );
|
|
|
+ const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.equal( formView );
|
|
|
+ return linkFeature.showPanel()
|
|
|
+ .then( () => {
|
|
|
+ expect( balloon.visibleView ).to.equal( formView );
|
|
|
+ sinon.assert.calledWithExactly( balloonAddSpy, {
|
|
|
+ view: formView,
|
|
|
+ position: {
|
|
|
+ target: selectedRange,
|
|
|
+ limiter: editorElement
|
|
|
+ }
|
|
|
+ } );
|
|
|
+ } );
|
|
|
} );
|
|
|
|
|
|
- it( 'should add link form to the ContextualBalloon and attach balloon to the link element ' +
|
|
|
- 'when collapsed selection is inside link element',
|
|
|
+ it( 'should add `formView` to the `ContextualBalloon` and attach panel to the link element when collapsed selection is inside ' +
|
|
|
+ 'link element',
|
|
|
() => {
|
|
|
- const balloonAddSpy = testUtils.sinon.spy( balloon, 'add' );
|
|
|
+ setModelData( editor.document, '<paragraph><$text linkHref="url">f[]oo</$text></paragraph>' );
|
|
|
+ const linkElement = editorElement.querySelector( 'a' );
|
|
|
|
|
|
- editor.document.schema.allow( { name: '$text', inside: '$root' } );
|
|
|
- setModelData( editor.document, '<$text linkHref="url">some[] url</$text>' );
|
|
|
- editor.editing.view.isFocused = true;
|
|
|
+ return linkFeature.showPanel()
|
|
|
+ .then( () => {
|
|
|
+ expect( balloon.visibleView ).to.equal( formView );
|
|
|
+ sinon.assert.calledWithExactly( balloonAddSpy, {
|
|
|
+ view: formView,
|
|
|
+ position: {
|
|
|
+ target: linkElement,
|
|
|
+ limiter: editorElement
|
|
|
+ }
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
|
|
|
- linkButton.fire( 'execute' );
|
|
|
+ it( 'should not focus `formView` at default', () => {
|
|
|
+ const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
|
|
|
|
|
|
- const linkElement = editorElement.querySelector( 'a' );
|
|
|
+ return linkFeature.showPanel()
|
|
|
+ .then( () => {
|
|
|
+ sinon.assert.notCalled( spy );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
|
|
|
- sinon.assert.calledWithExactly( balloonAddSpy, {
|
|
|
- view: formView,
|
|
|
- position: {
|
|
|
- target: linkElement,
|
|
|
- limiter: editorElement
|
|
|
- }
|
|
|
- } );
|
|
|
+ it( 'should not focus `formView` when is called with `false` parameter', () => {
|
|
|
+ const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
|
|
|
+
|
|
|
+ return linkFeature.showPanel( false )
|
|
|
+ .then( () => {
|
|
|
+ sinon.assert.notCalled( spy );
|
|
|
+ } );
|
|
|
} );
|
|
|
|
|
|
- it( 'should add link form to the ContextualBalloon and attach balloon to the selection, when selection is non-collapsed', () => {
|
|
|
- const balloonAddSpy = testUtils.sinon.spy( balloon, 'add' );
|
|
|
+ it( 'should not focus `formView` when is called with `true` parameter while balloon is opened but link form is not visible', () => {
|
|
|
+ const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
|
|
|
+ const viewMock = {
|
|
|
+ ready: true,
|
|
|
+ init: () => {},
|
|
|
+ destroy: () => {}
|
|
|
+ };
|
|
|
|
|
|
- editor.document.schema.allow( { name: '$text', inside: '$root' } );
|
|
|
- setModelData( editor.document, 'so[me ur]l' );
|
|
|
- editor.editing.view.isFocused = true;
|
|
|
+ return linkFeature.showPanel( false )
|
|
|
+ .then( () => balloon.add( { view: viewMock } ) )
|
|
|
+ .then( () => linkFeature.showPanel( true ) )
|
|
|
+ .then( () => {
|
|
|
+ sinon.assert.notCalled( spy );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
|
|
|
- linkButton.fire( 'execute' );
|
|
|
+ it( 'should focus `formView` when is called with `true` parameter', () => {
|
|
|
+ const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
|
|
|
|
|
|
- const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
|
|
|
+ return linkFeature.showPanel( true )
|
|
|
+ .then( () => {
|
|
|
+ sinon.assert.calledOnce( spy );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
|
|
|
- sinon.assert.calledWithExactly( balloonAddSpy, {
|
|
|
- view: formView,
|
|
|
- position: {
|
|
|
- target: selectedRange,
|
|
|
- limiter: editorElement
|
|
|
- }
|
|
|
- } );
|
|
|
+ it( 'should focus `formView` when is called with `true` parameter while balloon is opened and linkForm is visible', () => {
|
|
|
+ const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
|
|
|
+
|
|
|
+ return linkFeature.showPanel( false )
|
|
|
+ .then( () => linkFeature.showPanel( true ) )
|
|
|
+ .then( () => {
|
|
|
+ sinon.assert.calledOnce( spy );
|
|
|
+ } );
|
|
|
} );
|
|
|
|
|
|
- it( 'should select link input value when link balloon is opened', () => {
|
|
|
- const selectUrlInputSpy = testUtils.sinon.spy( linkFeature.formView.urlInputView, 'select' );
|
|
|
+ it( 'should keep editor ui focused when panel is shown with selected form', () => {
|
|
|
+ editor.ui.focusTracker.isFocused = false;
|
|
|
|
|
|
- editor.editing.view.isFocused = true;
|
|
|
+ // Open balloon panel with link inside.
|
|
|
+ return linkFeature.showPanel( true )
|
|
|
+ .then( () => {
|
|
|
+ // Check if editor ui is focused.
|
|
|
+ expect( editor.ui.focusTracker.isFocused ).to.true;
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'hidePanel()', () => {
|
|
|
+ beforeEach( () => {
|
|
|
+ return balloon.add( { view: formView } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should remove `formView` from the `ContextualBalloon` component', () => {
|
|
|
+ linkFeature.hidePanel();
|
|
|
+ expect( balloon.hasView( formView ) ).to.false;
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should not focus `editable` at default', () => {
|
|
|
+ const spy = testUtils.sinon.spy( editor.editing.view, 'focus' );
|
|
|
+
|
|
|
+ linkFeature.hidePanel();
|
|
|
+ sinon.assert.notCalled( spy );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should not focus `editable` when is called with `false` parameter', () => {
|
|
|
+ const spy = testUtils.sinon.spy( editor.editing.view, 'focus' );
|
|
|
+
|
|
|
+ linkFeature.hidePanel( false );
|
|
|
+ sinon.assert.notCalled( spy );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should focus `editable` when is called with `true` parameter', () => {
|
|
|
+ const spy = testUtils.sinon.spy( editor.editing.view, 'focus' );
|
|
|
+
|
|
|
+ linkFeature.hidePanel( true );
|
|
|
+ sinon.assert.calledOnce( spy );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should do not throw an error when `formView` is not added to the `balloon`', () => {
|
|
|
+ linkFeature.hidePanel( true );
|
|
|
+
|
|
|
+ expect( () => {
|
|
|
+ linkFeature.hidePanel( true );
|
|
|
+ } ).to.not.throw();
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should clear `render` listener from ViewDocument', () => {
|
|
|
+ const spy = sinon.spy();
|
|
|
+
|
|
|
+ linkFeature.listenTo( editor.editing.view, 'render', spy );
|
|
|
+
|
|
|
+ linkFeature.hidePanel();
|
|
|
+
|
|
|
+ editor.editing.view.render();
|
|
|
+
|
|
|
+ sinon.assert.notCalled( spy );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'link toolbar button', () => {
|
|
|
+ it( 'should register link button', () => {
|
|
|
+ expect( linkButton ).to.instanceOf( ButtonView );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should bind linkButtonView to link command', () => {
|
|
|
+ const command = editor.commands.get( 'link' );
|
|
|
+
|
|
|
+ command.isEnabled = true;
|
|
|
+ expect( linkButton.isEnabled ).to.be.true;
|
|
|
+
|
|
|
+ command.isEnabled = false;
|
|
|
+ expect( linkButton.isEnabled ).to.be.false;
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should show panel on execute event with selected `formView`', () => {
|
|
|
+ // Method is stubbed because it returns internal promise which can't be returned in test.
|
|
|
+ const spy = testUtils.sinon.stub( linkFeature, 'showPanel', () => {} );
|
|
|
|
|
|
linkButton.fire( 'execute' );
|
|
|
|
|
|
- // Focus of url input is called async after internal promise resolve and we are
|
|
|
- // not able to return this promise.
|
|
|
- return wait().then( () => {
|
|
|
- expect( selectUrlInputSpy.calledOnce ).to.true;
|
|
|
- } );
|
|
|
+ sinon.assert.calledWithExactly( spy, true );
|
|
|
} );
|
|
|
} );
|
|
|
|
|
|
@@ -172,42 +303,17 @@ describe( 'Link', () => {
|
|
|
} );
|
|
|
} );
|
|
|
|
|
|
- describe( 'ContextualBalloon', () => {
|
|
|
- let focusEditableSpy;
|
|
|
-
|
|
|
- beforeEach( () => {
|
|
|
- focusEditableSpy = testUtils.sinon.spy( editor.editing.view, 'focus' );
|
|
|
- } );
|
|
|
-
|
|
|
- it( 'should not be added to ContextualBalloon at default', () => {
|
|
|
- expect( balloon.visibleView ).to.null;
|
|
|
- } );
|
|
|
-
|
|
|
- it( 'should be added to ContextualBalloon and form should be selected on `CTRL+K` keystroke', () => {
|
|
|
- const selectUrlInputSpy = testUtils.sinon.spy( formView.urlInputView, 'select' );
|
|
|
+ describe( 'keyboard support', () => {
|
|
|
+ it( 'should show panel with selected `formView` on `CTRL+K` keystroke', () => {
|
|
|
+ // Method is stubbed because it returns internal promise which can't be returned in test.
|
|
|
+ const spy = testUtils.sinon.stub( linkFeature, 'showPanel', () => {} );
|
|
|
|
|
|
editor.keystrokes.press( { keyCode: keyCodes.k, ctrlKey: true } );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.equal( formView );
|
|
|
-
|
|
|
- // Focus of url input is called async after internal promise resolve and we are
|
|
|
- // not able to return this promise.
|
|
|
- return wait().then( () => {
|
|
|
- expect( selectUrlInputSpy.calledOnce ).to.true;
|
|
|
- } );
|
|
|
- } );
|
|
|
-
|
|
|
- it( 'should not add panel to ContextualBalloon more than once', () => {
|
|
|
- // Add panel to balloon by pressing toolbar button.
|
|
|
- linkButton.fire( 'execute' );
|
|
|
-
|
|
|
- // Press button once again.
|
|
|
- expect( () => {
|
|
|
- linkButton.fire( 'execute' );
|
|
|
- } ).to.not.throw();
|
|
|
+ sinon.assert.calledWithExactly( spy, true );
|
|
|
} );
|
|
|
|
|
|
- it( 'should focus the link form on Tab key press', () => {
|
|
|
+ it( 'should focus the `formView` on `Tab` key press when panel is open', () => {
|
|
|
const keyEvtData = {
|
|
|
keyCode: keyCodes.tab,
|
|
|
preventDefault: sinon.spy(),
|
|
|
@@ -225,7 +331,7 @@ describe( 'Link', () => {
|
|
|
sinon.assert.notCalled( spy );
|
|
|
|
|
|
// Balloon is visible, form focused.
|
|
|
- return balloon.add( { view: formView } )
|
|
|
+ return linkFeature.showPanel( true )
|
|
|
.then( () => {
|
|
|
formView.focusTracker.isFocused = true;
|
|
|
|
|
|
@@ -244,122 +350,109 @@ describe( 'Link', () => {
|
|
|
} );
|
|
|
} );
|
|
|
|
|
|
- it( 'should keep editor ui focused when link form has focus', () => {
|
|
|
- editor.ui.focusTracker.isFocused = false;
|
|
|
-
|
|
|
- // Open balloon panel with link inside.
|
|
|
- linkButton.fire( 'execute' );
|
|
|
+ it( 'should hide panel after Esc key press (from editor) and not focus editable', () => {
|
|
|
+ const spy = testUtils.sinon.spy( linkFeature, 'hidePanel' );
|
|
|
+ const keyEvtData = {
|
|
|
+ keyCode: keyCodes.esc,
|
|
|
+ preventDefault: sinon.spy(),
|
|
|
+ stopPropagation: sinon.spy()
|
|
|
+ };
|
|
|
|
|
|
- // Be sure that form view is focused.
|
|
|
- formView.element.dispatchEvent( new Event( 'focus' ) );
|
|
|
+ // Balloon is visible.
|
|
|
+ return linkFeature.showPanel( false ).then( () => {
|
|
|
+ editor.keystrokes.press( keyEvtData );
|
|
|
|
|
|
- // Check if editor ui is focused.
|
|
|
- expect( editor.ui.focusTracker.isFocused ).to.true;
|
|
|
+ sinon.assert.calledWithExactly( spy );
|
|
|
+ } );
|
|
|
} );
|
|
|
|
|
|
- describe( 'close listeners', () => {
|
|
|
- describe( 'keyboard', () => {
|
|
|
- it( 'should close after Esc key press (from editor) and not focus editable', () => {
|
|
|
- const keyEvtData = {
|
|
|
- keyCode: keyCodes.esc,
|
|
|
- preventDefault: sinon.spy(),
|
|
|
- stopPropagation: sinon.spy()
|
|
|
- };
|
|
|
+ it( 'should not hide panel after Esc key press (from editor) when panel is open but is not visible', () => {
|
|
|
+ const spy = testUtils.sinon.spy( linkFeature, 'hidePanel' );
|
|
|
+ const keyEvtData = {
|
|
|
+ keyCode: keyCodes.esc,
|
|
|
+ preventDefault: () => {},
|
|
|
+ stopPropagation: () => {}
|
|
|
+ };
|
|
|
|
|
|
- // Balloon is visible.
|
|
|
- return balloon.add( { view: formView } ).then( () => {
|
|
|
- editor.keystrokes.press( keyEvtData );
|
|
|
+ const viewMock = {
|
|
|
+ ready: true,
|
|
|
+ init: () => {},
|
|
|
+ destroy: () => {}
|
|
|
+ };
|
|
|
|
|
|
- expect( balloon.visibleView ).to.null;
|
|
|
- sinon.assert.notCalled( focusEditableSpy );
|
|
|
- } );
|
|
|
- } );
|
|
|
+ return linkFeature.showPanel( false )
|
|
|
+ .then( () => balloon.add( { view: viewMock } ) )
|
|
|
+ .then( () => {
|
|
|
+ editor.keystrokes.press( keyEvtData );
|
|
|
|
|
|
- it( 'should not close after Esc key press (from editor) when panel is in stack but not visible', () => {
|
|
|
- const keyEvtData = {
|
|
|
- keyCode: keyCodes.esc,
|
|
|
- preventDefault: () => {},
|
|
|
- stopPropagation: () => {}
|
|
|
- };
|
|
|
-
|
|
|
- const viewMock = {
|
|
|
- init: () => {},
|
|
|
- destroy: () => {}
|
|
|
- };
|
|
|
-
|
|
|
- return balloon.add( { view: formView } )
|
|
|
- .then( () => {
|
|
|
- return balloon.add( { view: viewMock } );
|
|
|
- } )
|
|
|
- .then( () => {
|
|
|
- editor.keystrokes.press( keyEvtData );
|
|
|
-
|
|
|
- expect( balloon.visibleView ).to.equal( viewMock );
|
|
|
- expect( balloon.hasView( formView ) ).to.true;
|
|
|
- sinon.assert.notCalled( focusEditableSpy );
|
|
|
- } );
|
|
|
+ sinon.assert.notCalled( spy );
|
|
|
} );
|
|
|
+ } );
|
|
|
|
|
|
- it( 'should close after Esc key press (from the form) and focus editable', () => {
|
|
|
- const keyEvtData = {
|
|
|
- keyCode: keyCodes.esc,
|
|
|
- preventDefault: sinon.spy(),
|
|
|
- stopPropagation: sinon.spy()
|
|
|
- };
|
|
|
+ it( 'should hide panel after Esc key press (from the form) and focus editable', () => {
|
|
|
+ const spy = testUtils.sinon.spy( linkFeature, 'hidePanel' );
|
|
|
+ const keyEvtData = {
|
|
|
+ keyCode: keyCodes.esc,
|
|
|
+ preventDefault: sinon.spy(),
|
|
|
+ stopPropagation: sinon.spy()
|
|
|
+ };
|
|
|
|
|
|
- return balloon.add( { view: formView } )
|
|
|
- .then( () => {
|
|
|
- formView.keystrokes.press( keyEvtData );
|
|
|
+ return linkFeature.showPanel( true )
|
|
|
+ .then( () => {
|
|
|
+ formView.keystrokes.press( keyEvtData );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.null;
|
|
|
- sinon.assert.calledOnce( focusEditableSpy );
|
|
|
- } );
|
|
|
+ sinon.assert.calledWithExactly( spy, true );
|
|
|
} );
|
|
|
- } );
|
|
|
+ } );
|
|
|
+ } );
|
|
|
+
|
|
|
+ describe( 'mouse support', () => {
|
|
|
+ it( 'should hide panel and not focus editable on click outside the panel', () => {
|
|
|
+ const spy = testUtils.sinon.spy( linkFeature, 'hidePanel' );
|
|
|
|
|
|
- describe( 'mouse', () => {
|
|
|
- it( 'should close and not focus editable on click outside the panel', () => {
|
|
|
- return balloon.add( { view: formView } )
|
|
|
- .then( () => {
|
|
|
- document.body.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
|
|
|
+ return linkFeature.showPanel( true )
|
|
|
+ .then( () => {
|
|
|
+ document.body.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.null;
|
|
|
- expect( focusEditableSpy.notCalled ).to.true;
|
|
|
- } );
|
|
|
+ sinon.assert.calledWithExactly( spy );
|
|
|
} );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should not hide panel on click inside the panel', () => {
|
|
|
+ const spy = testUtils.sinon.spy( linkFeature, 'hidePanel' );
|
|
|
|
|
|
- it( 'should not close on click inside the panel', () => {
|
|
|
- return balloon.add( { view: formView } )
|
|
|
- .then( () => {
|
|
|
- balloon.view.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
|
|
|
+ return linkFeature.showPanel( true )
|
|
|
+ .then( () => {
|
|
|
+ balloon.view.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.equal( formView );
|
|
|
- } );
|
|
|
+ sinon.assert.notCalled( spy );
|
|
|
} );
|
|
|
- } );
|
|
|
} );
|
|
|
|
|
|
- describe( 'click on editable', () => {
|
|
|
- it( 'should open with not selected url input when collapsed selection is inside link element', () => {
|
|
|
- const selectUrlInputSpy = testUtils.sinon.spy( formView.urlInputView, 'select' );
|
|
|
- const observer = editor.editing.view.getObserver( ClickObserver );
|
|
|
+ describe( 'clicking on editable', () => {
|
|
|
+ let observer;
|
|
|
+
|
|
|
+ beforeEach( () => {
|
|
|
+ observer = editor.editing.view.getObserver( ClickObserver );
|
|
|
+ } );
|
|
|
+
|
|
|
+ it( 'should open with not selected formView when collapsed selection is inside link element', () => {
|
|
|
+ // Method is stubbed because it returns internal promise which can't be returned in test.
|
|
|
+ const spy = testUtils.sinon.stub( linkFeature, 'showPanel', () => {} );
|
|
|
|
|
|
editor.document.schema.allow( { name: '$text', inside: '$root' } );
|
|
|
setModelData( editor.document, '<$text linkHref="url">fo[]o</$text>' );
|
|
|
|
|
|
observer.fire( 'click', { target: document.body } );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.equal( formView );
|
|
|
-
|
|
|
- // Focus of url input is called async after internal promise resolve and we are
|
|
|
- // not able to return this promise.
|
|
|
- return wait().then( () => {
|
|
|
- expect( selectUrlInputSpy.notCalled ).to.true;
|
|
|
- } );
|
|
|
+ sinon.assert.calledWithExactly( spy );
|
|
|
} );
|
|
|
|
|
|
it( 'should keep open and update position until collapsed selection stay inside the same link element', () => {
|
|
|
- const observer = editor.editing.view.getObserver( ClickObserver );
|
|
|
+ // Method is stubbed because it returns internal promise which can't be returned in test.
|
|
|
+ const showSpy = testUtils.sinon.stub( linkFeature, 'showPanel', () => {} );
|
|
|
+ const hideSpy = testUtils.sinon.stub( linkFeature, 'hidePanel' );
|
|
|
+ const updatePositionSpy = testUtils.sinon.stub( balloon, 'updatePosition', () => {} );
|
|
|
|
|
|
editor.document.schema.allow( { name: '$text', inside: '$root' } );
|
|
|
setModelData( editor.document, '<$text linkHref="url">b[]ar</$text>' );
|
|
|
@@ -369,22 +462,24 @@ describe( 'Link', () => {
|
|
|
|
|
|
observer.fire( 'click', { target: document.body } );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.equal( formView );
|
|
|
-
|
|
|
- const updatePositionSpy = testUtils.sinon.spy( balloon, 'updatePosition' );
|
|
|
+ // Panel is shown.
|
|
|
+ sinon.assert.calledOnce( showSpy );
|
|
|
|
|
|
// Move selection.
|
|
|
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 1 ) ], true );
|
|
|
editor.editing.view.render();
|
|
|
|
|
|
- // Check if balloon is still open and position was updated.
|
|
|
- expect( balloon.visibleView ).to.equal( formView );
|
|
|
- expect( updatePositionSpy.calledOnce ).to.true;
|
|
|
+ // Check if balloon is still opened (wasn't hide).
|
|
|
+ sinon.assert.notCalled( hideSpy );
|
|
|
+ // And position was updated
|
|
|
+ sinon.assert.calledOnce( updatePositionSpy );
|
|
|
} );
|
|
|
|
|
|
it( 'should not duplicate `render` listener on `ViewDocument`', () => {
|
|
|
- const observer = editor.editing.view.getObserver( ClickObserver );
|
|
|
- const updatePositionSpy = testUtils.sinon.spy( balloon, 'updatePosition' );
|
|
|
+ const updatePositionSpy = testUtils.sinon.stub( balloon, 'updatePosition', () => {} );
|
|
|
+
|
|
|
+ // Method is stubbed because it returns internal promise which can't be returned in test.
|
|
|
+ testUtils.sinon.stub( linkFeature, 'showPanel', () => {} );
|
|
|
|
|
|
editor.document.schema.allow( { name: '$text', inside: '$root' } );
|
|
|
setModelData( editor.document, '<$text linkHref="url">b[]ar</$text>' );
|
|
|
@@ -408,7 +503,10 @@ describe( 'Link', () => {
|
|
|
} );
|
|
|
|
|
|
it( 'should close when selection goes outside the link element', () => {
|
|
|
- const observer = editor.editing.view.getObserver( ClickObserver );
|
|
|
+ const hideSpy = testUtils.sinon.stub( linkFeature, 'hidePanel' );
|
|
|
+
|
|
|
+ // Method is stubbed because it returns internal promise which can't be returned in test.
|
|
|
+ testUtils.sinon.stub( linkFeature, 'showPanel', () => {} );
|
|
|
|
|
|
editor.document.schema.allow( { name: '$text', inside: '$root' } );
|
|
|
setModelData( editor.document, 'foo <$text linkHref="url">b[]ar</$text>' );
|
|
|
@@ -418,16 +516,19 @@ describe( 'Link', () => {
|
|
|
|
|
|
observer.fire( 'click', { target: document.body } );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.equal( formView );
|
|
|
+ sinon.assert.notCalled( hideSpy );
|
|
|
|
|
|
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 3, text, 3 ) ], true );
|
|
|
editor.editing.view.render();
|
|
|
|
|
|
- expect( balloon.visibleView ).to.null;
|
|
|
+ sinon.assert.calledOnce( hideSpy );
|
|
|
} );
|
|
|
|
|
|
it( 'should close when selection goes to the other link element with the same href', () => {
|
|
|
- const observer = editor.editing.view.getObserver( ClickObserver );
|
|
|
+ const hideSpy = testUtils.sinon.stub( linkFeature, 'hidePanel' );
|
|
|
+
|
|
|
+ // Method is stubbed because it returns internal promise which can't be returned in test.
|
|
|
+ testUtils.sinon.stub( linkFeature, 'showPanel', () => {} );
|
|
|
|
|
|
editor.document.schema.allow( { name: '$text', inside: '$root' } );
|
|
|
setModelData( editor.document, '<$text linkHref="url">f[]oo</$text> bar <$text linkHref="url">biz</$text>' );
|
|
|
@@ -437,16 +538,19 @@ describe( 'Link', () => {
|
|
|
|
|
|
observer.fire( 'click', { target: document.body } );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.equal( formView );
|
|
|
+ sinon.assert.notCalled( hideSpy );
|
|
|
|
|
|
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 1 ) ], true );
|
|
|
editor.editing.view.render();
|
|
|
|
|
|
- expect( balloon.visibleView ).to.null;
|
|
|
+ sinon.assert.calledOnce( hideSpy );
|
|
|
} );
|
|
|
|
|
|
it( 'should close when selection becomes non-collapsed', () => {
|
|
|
- const observer = editor.editing.view.getObserver( ClickObserver );
|
|
|
+ const hideSpy = testUtils.sinon.stub( linkFeature, 'hidePanel' );
|
|
|
+
|
|
|
+ // Method is stubbed because it returns internal promise which can't be returned in test.
|
|
|
+ testUtils.sinon.stub( linkFeature, 'showPanel', () => {} );
|
|
|
|
|
|
editor.document.schema.allow( { name: '$text', inside: '$root' } );
|
|
|
setModelData( editor.document, '<$text linkHref="url">f[]oo</$text>' );
|
|
|
@@ -456,58 +560,31 @@ describe( 'Link', () => {
|
|
|
|
|
|
observer.fire( 'click', { target: {} } );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.equal( formView );
|
|
|
-
|
|
|
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 2 ) ] );
|
|
|
editor.editing.view.render();
|
|
|
|
|
|
- expect( balloon.visibleView ).to.null;
|
|
|
- } );
|
|
|
-
|
|
|
- it( 'should stop updating position after close', () => {
|
|
|
- const observer = editor.editing.view.getObserver( ClickObserver );
|
|
|
-
|
|
|
- editor.document.schema.allow( { name: '$text', inside: '$root' } );
|
|
|
- setModelData( editor.document, '<$text linkHref="url">b[]ar</$text>' );
|
|
|
-
|
|
|
- const root = editor.editing.view.getRoot();
|
|
|
- const text = root.getChild( 0 ).getChild( 0 );
|
|
|
-
|
|
|
- observer.fire( 'click', { target: {} } );
|
|
|
-
|
|
|
- expect( balloon.visibleView ).to.equal( formView );
|
|
|
-
|
|
|
- // Close balloon by dispatching `cancel` event on formView.
|
|
|
- formView.fire( 'cancel' );
|
|
|
-
|
|
|
- const updatePositionSpy = testUtils.sinon.spy( balloon, 'updatePosition' );
|
|
|
-
|
|
|
- // Move selection inside link element.
|
|
|
- editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 2, text, 2 ) ], true );
|
|
|
- editor.editing.view.render();
|
|
|
-
|
|
|
- expect( updatePositionSpy.notCalled ).to.true;
|
|
|
+ sinon.assert.calledOnce( hideSpy );
|
|
|
} );
|
|
|
|
|
|
it( 'should not open when selection is not inside link element', () => {
|
|
|
- const observer = editor.editing.view.getObserver( ClickObserver );
|
|
|
+ const showSpy = testUtils.sinon.stub( linkFeature, 'showPanel' );
|
|
|
|
|
|
setModelData( editor.document, '[]' );
|
|
|
|
|
|
observer.fire( 'click', { target: {} } );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.null;
|
|
|
+ sinon.assert.notCalled( showSpy );
|
|
|
} );
|
|
|
|
|
|
it( 'should not open when selection is non-collapsed', () => {
|
|
|
- const observer = editor.editing.view.getObserver( ClickObserver );
|
|
|
+ const showSpy = testUtils.sinon.stub( linkFeature, 'showPanel' );
|
|
|
|
|
|
editor.document.schema.allow( { name: '$text', inside: '$root' } );
|
|
|
setModelData( editor.document, '<$text linkHref="url">f[o]o</$text>' );
|
|
|
|
|
|
- observer.fire( 'click', { target: document.body } );
|
|
|
+ observer.fire( 'click', { target: {} } );
|
|
|
|
|
|
- expect( balloon.visibleView ).to.null;
|
|
|
+ sinon.assert.notCalled( showSpy );
|
|
|
} );
|
|
|
} );
|
|
|
} );
|
|
|
@@ -543,7 +620,7 @@ describe( 'Link', () => {
|
|
|
} );
|
|
|
|
|
|
it( 'should hide and focus editable on formView#submit event', () => {
|
|
|
- return balloon.add( { view: formView } )
|
|
|
+ return linkFeature.showPanel()
|
|
|
.then( () => {
|
|
|
formView.fire( 'submit' );
|
|
|
|
|
|
@@ -562,7 +639,7 @@ describe( 'Link', () => {
|
|
|
} );
|
|
|
|
|
|
it( 'should hide and focus editable on formView#unlink event', () => {
|
|
|
- return balloon.add( { view: formView } )
|
|
|
+ return linkFeature.showPanel()
|
|
|
.then( () => {
|
|
|
formView.fire( 'unlink' );
|
|
|
|
|
|
@@ -572,7 +649,7 @@ describe( 'Link', () => {
|
|
|
} );
|
|
|
|
|
|
it( 'should hide and focus editable on formView#cancel event', () => {
|
|
|
- return balloon.add( { view: formView } )
|
|
|
+ return linkFeature.showPanel()
|
|
|
.then( () => {
|
|
|
formView.fire( 'cancel' );
|
|
|
|
|
|
@@ -583,9 +660,3 @@ describe( 'Link', () => {
|
|
|
} );
|
|
|
} );
|
|
|
} );
|
|
|
-
|
|
|
-function wait( delay = 1 ) {
|
|
|
- return new Promise( ( resolve ) => {
|
|
|
- setTimeout( () => resolve(), delay );
|
|
|
- } );
|
|
|
-}
|