/** * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ /* global document */ import ToolbarView from 'ckeditor5-ui/src/toolbar/toolbarview'; import KeystrokeHandler from 'ckeditor5-utils/src/keystrokehandler'; import FocusTracker from 'ckeditor5-utils/src/focustracker'; import FocusCycler from 'ckeditor5-ui/src/focuscycler'; import { keyCodes } from 'ckeditor5-utils/src/keyboard'; import ViewCollection from 'ckeditor5-ui/src/viewcollection'; import View from 'ckeditor5-ui/src/view'; describe( 'ToolbarView', () => { let locale, view; beforeEach( () => { locale = {}; view = new ToolbarView( locale ); return view.init(); } ); describe( 'constructor()', () => { it( 'should set view#locale', () => { expect( view.locale ).to.equal( locale ); } ); it( 'should create view#children collection', () => { expect( view.items ).to.be.instanceOf( ViewCollection ); } ); it( 'creates #focusTracker instance', () => { expect( view.focusTracker ).to.be.instanceOf( FocusTracker ); } ); it( 'creates #keystrokes instance', () => { expect( view.keystrokes ).to.be.instanceOf( KeystrokeHandler ); } ); it( 'creates #_focusCycler instance', () => { expect( view._focusCycler ).to.be.instanceOf( FocusCycler ); } ); it( 'registers #items in #focusTracker', () => { const spyAdd = sinon.spy( view.focusTracker, 'add' ); const spyRemove = sinon.spy( view.focusTracker, 'remove' ); view.items.add( focusable() ); view.items.add( focusable() ); sinon.assert.calledTwice( spyAdd ); view.items.remove( 1 ); sinon.assert.calledOnce( spyRemove ); } ); } ); describe( 'template', () => { it( 'should create element from template', () => { expect( view.element.classList.contains( 'ck-toolbar' ) ).to.true; } ); } ); describe( 'init()', () => { it( 'starts listening for #keystrokes coming from #element', () => { view = new ToolbarView(); const spy = sinon.spy( view.keystrokes, 'listenTo' ); return view.init().then( () => { sinon.assert.calledOnce( spy ); sinon.assert.calledWithExactly( spy, view.element ); } ); } ); describe( 'activates keyboard navigation for the toolbar', () => { it( 'so "arrowup" focuses previous focusable item', () => { const keyEvtData = { keyCode: keyCodes.arrowup, preventDefault: sinon.spy(), stopPropagation: sinon.spy() }; // No children to focus. view.keystrokes.press( keyEvtData ); sinon.assert.calledOnce( keyEvtData.preventDefault ); sinon.assert.calledOnce( keyEvtData.stopPropagation ); view.items.add( nonFocusable() ); view.items.add( nonFocusable() ); // No focusable children. view.keystrokes.press( keyEvtData ); sinon.assert.calledTwice( keyEvtData.preventDefault ); sinon.assert.calledTwice( keyEvtData.stopPropagation ); view.items.add( focusable() ); view.items.add( nonFocusable() ); view.items.add( focusable() ); // Mock the last item is focused. view.focusTracker.isFocused = true; view.focusTracker.focusedElement = view.items.get( 4 ).element; const spy = sinon.spy( view.items.get( 2 ), 'focus' ); view.keystrokes.press( keyEvtData ); sinon.assert.calledThrice( keyEvtData.preventDefault ); sinon.assert.calledThrice( keyEvtData.stopPropagation ); sinon.assert.calledOnce( spy ); } ); it( 'so "arrowleft" focuses previous focusable item', () => { const keyEvtData = { keyCode: keyCodes.arrowleft, preventDefault: sinon.spy(), stopPropagation: sinon.spy() }; view.items.add( focusable() ); view.items.add( nonFocusable() ); view.items.add( focusable() ); // Mock the last item is focused. view.focusTracker.isFocused = true; view.focusTracker.focusedElement = view.items.get( 2 ).element; const spy = sinon.spy( view.items.get( 0 ), 'focus' ); view.keystrokes.press( keyEvtData ); sinon.assert.calledOnce( spy ); } ); it( 'so "arrowdown" focuses next focusable item', () => { const keyEvtData = { keyCode: keyCodes.arrowdown, preventDefault: sinon.spy(), stopPropagation: sinon.spy() }; // No children to focus. view.keystrokes.press( keyEvtData ); sinon.assert.calledOnce( keyEvtData.preventDefault ); sinon.assert.calledOnce( keyEvtData.stopPropagation ); view.items.add( nonFocusable() ); view.items.add( nonFocusable() ); // No focusable children. view.keystrokes.press( keyEvtData ); sinon.assert.calledTwice( keyEvtData.preventDefault ); sinon.assert.calledTwice( keyEvtData.stopPropagation ); view.items.add( focusable() ); view.items.add( nonFocusable() ); view.items.add( focusable() ); // Mock the last item is focused. view.focusTracker.isFocused = true; view.focusTracker.focusedElement = view.items.get( 4 ).element; const spy = sinon.spy( view.items.get( 2 ), 'focus' ); view.keystrokes.press( keyEvtData ); sinon.assert.calledThrice( keyEvtData.preventDefault ); sinon.assert.calledThrice( keyEvtData.stopPropagation ); sinon.assert.calledOnce( spy ); } ); it( 'so "arrowright" focuses next focusable item', () => { const keyEvtData = { keyCode: keyCodes.arrowright, preventDefault: sinon.spy(), stopPropagation: sinon.spy() }; view.items.add( focusable() ); view.items.add( nonFocusable() ); view.items.add( focusable() ); // Mock the last item is focused. view.focusTracker.isFocused = true; view.focusTracker.focusedElement = view.items.get( 0 ).element; const spy = sinon.spy( view.items.get( 2 ), 'focus' ); view.keystrokes.press( keyEvtData ); sinon.assert.calledOnce( spy ); } ); } ); } ); describe( 'focus()', () => { it( 'focuses the first focusable item in DOM', () => { // No children to focus. view.focus(); // The second child is focusable. view.items.add( nonFocusable() ); view.items.add( focusable() ); view.items.add( nonFocusable() ); const spy = sinon.spy( view.items.get( 1 ), 'focus' ); view.focus(); sinon.assert.calledOnce( spy ); } ); } ); } ); function focusable() { const view = nonFocusable(); view.focus = () => {}; return view; } function nonFocusable() { const view = new View(); view.element = document.createElement( 'li' ); return view; }