/*global KeyboardManager, sinon, KeyboardHelper, MockKeyboardHelper, MocksHelper, MockNavigatorSettings, Applications, MockL10n, MockImeMenu, inputWindowManager, TYPE_GROUP_MAPPING, InputLayouts, MockPromise, MockEventTarget */ 'use strict'; require('/shared/test/unit/mocks/mock_service.js'); requireApp('system/test/unit/mock_lazy_loader.js'); require('/shared/test/unit/mocks/mock_keyboard_helper.js'); require('/shared/test/unit/mocks/mock_navigator_moz_settings.js'); require('/shared/test/unit/mocks/mock_l10n.js'); require('/shared/test/unit/mocks/mock_promise.js'); require('/test/unit/mock_applications.js'); require('/test/unit/mock_homescreen_launcher.js'); require('/test/unit/mock_ime_switcher.js'); require('/test/unit/mock_ime_menu.js'); require('/js/input_layouts.js'); require('/test/unit/mock_input_window_manager.js'); require('/js/keyboard_manager.js'); require('/shared/test/unit/mocks/mock_event_target.js'); var mocksHelperForKeyboardManager = new MocksHelper([ 'KeyboardHelper', 'LazyLoader', 'Applications', 'IMESwitcher', 'ImeMenu', 'L10n', 'Service', 'InputWindowManager' ]).init(); suite('KeyboardManager', function() { var SWITCH_CHANGE_DELAY = 20; function createMockInputContextFocusEvent(inputType) { return { type: 'inputcontextfocus', detail: { type: 'input', inputType: inputType }, defaultPrevented: false, preventDefault: function() { this.defaultPrevented = true; } }; } function simulateInputContextFocusEvent(inputType) { // we call the method directly because we can't send a direct event // because otherwise in this test, we'll have n mozChromeEvent listeners KeyboardManager.handleEvent(createMockInputContextFocusEvent(inputType)); } function simulateInputContextBlurEvent() { // we call the method directly because we can't send a direct event // because otherwise in this test, we'll have n mozChromeEvent listeners KeyboardManager.handleEvent({ type: 'inputcontextblur', defaultPrevented: false, preventDefault: function() { this.defaultPrevented = true; } }); } function setupHTML() { var rc = document.querySelector('#run-container'); rc.innerHTML = ''; rc.innerHTML += '
hoi
'; } mocksHelperForKeyboardManager.attachTestHelpers(); var realMozSettings = null; var realMozInputMethod = null; var stubGetGroupCurrentActiveLayoutIndexAsync; suiteSetup(function() { document.body.innerHTML += '
'; realMozSettings = navigator.mozSettings; navigator.mozSettings = MockNavigatorSettings; realMozInputMethod = navigator.mozInputMethod; window.DynamicInputRegistry = function() {}; window.DynamicInputRegistry.prototype.start = function() {}; }); suiteTeardown(function() { navigator.mozSettings = realMozSettings; navigator.mozInputMethod = realMozInputMethod; }); setup(function() { // we use sinon's fake timers for all the tests so that the timeouts used in // the tested code in one test don't disturb the next test. this.sinon.useFakeTimers(); setupHTML(); navigator.mozInputMethod = { mgmt: new MockEventTarget() }; navigator.mozInputMethod.mgmt.setSupportsSwitchingTypes = this.sinon.stub(); // stub this such that the mocked SettingListener's callback would not // trigger out of the blue when sinon fake timer advances this.sinon.stub(KeyboardManager, '_tryLaunchOnBoot'); // we test these InputLayouts methods separately in input_layouts_test.js this.sinon.stub(InputLayouts.prototype, '_getSettings'); stubGetGroupCurrentActiveLayoutIndexAsync = this.sinon.stub(InputLayouts.prototype, 'getGroupCurrentActiveLayoutIndexAsync'); KeyboardManager.start(); this.sinon.stub( window.inputWindowManager, 'getLoadedManifestURLs').returns([]); this.sinon.stub( window.inputWindowManager, '_onInputLayoutsRemoved').returns(null); this.sinon.stub(window.inputWindowManager, 'showInputWindow'); this.sinon.stub(window.inputWindowManager, 'hideInputWindow'); this.sinon.stub(window.inputWindowManager, 'preloadInputWindow'); window.applications = Applications; window.applications.mRegisterMockApp({ manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', manifest: { type: 'certified' } }); Applications.mRegisterMockApp({ manifestURL: 'app://keyboard-test.gaiamobile.org/manifest.webapp', manifest: { type: 'certified' } }); }); teardown(function() { stubGetGroupCurrentActiveLayoutIndexAsync.restore(); navigator.mozInputMethod = realMozInputMethod; }); suite('Switching keyboard focus', function() { setup(function() { this.sinon.stub(KeyboardManager, '_showIMESwitcher'); this.sinon.stub(KeyboardManager, '_setKeyboardToShow'); }); test('The inputcontextfocus event triggers _handleInputContextFocus', function() { this.sinon.stub(KeyboardManager, '_handleInputContextFocus'); var event = createMockInputContextFocusEvent('text'); navigator.mozInputMethod.mgmt.dispatchEvent(event); sinon.assert.called(KeyboardManager._handleInputContextFocus); assert.isTrue(event.defaultPrevented); }); suite('Switching inputType', function() { var p1; var p2; setup(function() { this.getLayouts = this.sinon.stub(KeyboardHelper, 'getLayouts'); this.checkDefaults = this.sinon.stub(KeyboardHelper, 'checkDefaults'); MockKeyboardHelper.watchCallback(KeyboardHelper.layouts, { apps: true }); p1 = new MockPromise(); p2 = new MockPromise(); stubGetGroupCurrentActiveLayoutIndexAsync .onFirstCall().returns(p1) .onSecondCall().returns(p2); }); test('Switching from "text" to "number"', function() { simulateInputContextFocusEvent('text'); p1.then.getCall(0).args[0](undefined); simulateInputContextFocusEvent('number'); p2.then.getCall(0).args[0](undefined); sinon.assert.calledWith(KeyboardManager._setKeyboardToShow, 'text'); sinon.assert.calledWith(KeyboardManager._setKeyboardToShow, 'number'); }); test('Switching from "text" to "text"', function() { simulateInputContextFocusEvent('text'); p1.then.getCall(0).args[0](undefined); simulateInputContextFocusEvent('text'); p2.then.getCall(0).args[0](undefined); sinon.assert.calledWith(KeyboardManager._setKeyboardToShow, 'text'); }); test('Switching from "text" to "select-one"', function() { simulateInputContextFocusEvent('text'); p1.then.getCall(0).args[0](undefined); simulateInputContextFocusEvent('select-one'); sinon.assert.called(inputWindowManager.hideInputWindow); }); }); suite('keyboard type "url" - has enabled layouts', function() { setup(function() { this.getLayouts = this.sinon.stub(KeyboardHelper, 'getLayouts'); this.checkDefaults = this.sinon.stub(KeyboardHelper, 'checkDefaults'); MockKeyboardHelper.watchCallback(KeyboardHelper.layouts, { apps: true }); var p = new MockPromise(); stubGetGroupCurrentActiveLayoutIndexAsync.returns(p); simulateInputContextFocusEvent('url'); p.then.getCall(0).args[0](undefined); }); test('does not request layouts or defaults', function() { assert.isFalse(this.getLayouts.called); assert.isFalse(this.checkDefaults.called); }); test('shows "url" keyboard', function() { assert.ok(KeyboardManager._setKeyboardToShow.calledWith('url')); }); }); suite('keyboard type "url" - no enabled layout', function() { setup(function() { this.saveToSettings = this.sinon.stub(KeyboardHelper, 'saveToSettings'); this.getLayouts = this.sinon.stub(KeyboardHelper, 'getLayouts'); // make this respond automatically this.getLayouts.yields([]); // trigger no keyboards in the first place MockKeyboardHelper.watchCallback([], { apps: true, settings: true }); KeyboardManager.inputLayouts.layouts = { text: { activeLayout: undefined } }; }); teardown(function() { MockKeyboardHelper.watchCallback(KeyboardHelper.layouts, { apps: true }); }); suite('no defaults enabled', function() { setup(function() { this.checkDefaults = this.sinon.stub(KeyboardHelper, 'checkDefaults'); var p = new MockPromise(); stubGetGroupCurrentActiveLayoutIndexAsync.returns(p); simulateInputContextFocusEvent('url'); this.checkDefaults.getCall(0).args[0](); p.then.getCall(0).args[0](undefined); }); test('requests defaults', function() { assert.ok(this.checkDefaults.called); }); test('requests layouts', function() { assert.ok(this.getLayouts.calledAfter(this.checkDefaults)); }); test('reverts to "text" when no defaults', function() { assert.ok(KeyboardManager._setKeyboardToShow.calledWith('text')); }); }); suite('defaults enabled', function() { setup(function() { this.checkDefaults = this.sinon.stub(KeyboardHelper, 'checkDefaults', function overrideCheckDefaults(callback) { this.getLayouts.yields([KeyboardHelper.layouts[0]]); callback(); }.bind(this)); var p = new MockPromise(); stubGetGroupCurrentActiveLayoutIndexAsync.returns(p); simulateInputContextFocusEvent('url'); p.then.getCall(0).args[0](undefined); }); test('requests layouts', function() { assert.ok(this.getLayouts.called); }); test('requests defaults', function() { assert.ok(this.checkDefaults.called); }); test('requests layouts again', function() { assert.ok(this.getLayouts.calledAfter(this.checkDefaults)); }); test('saves', function() { assert.ok(this.saveToSettings.called); }); test('keeps "url" when defaults found', function() { assert.ok(KeyboardManager._setKeyboardToShow.calledWith('url')); }); }); }); suite('Restore user selection from settings', function() { var km, p; setup(function() { km = KeyboardManager; p = new MockPromise(); TYPE_GROUP_MAPPING.chocola = 'chocola'; km.inputLayouts.layouts.chocola = [ { id: 'default', manifestURL: 'app://default' }, { id: 'trahlah', manifestURL: 'app://yolo' }, { id: 'another', manifestURL: 'app://yolo' } ]; stubGetGroupCurrentActiveLayoutIndexAsync.returns(p); }); test('Selection is present', function() { simulateInputContextFocusEvent('chocola'); p.then.getCall(0).args[0](1); sinon.assert.calledWith(km._setKeyboardToShow, 'chocola', 1); }); test('Selection is present, multiple from same manifest', function() { simulateInputContextFocusEvent('chocola'); p.then.getCall(0).args[0](2); sinon.assert.calledWith(km._setKeyboardToShow, 'chocola', 2); }); test('Selection is not present or not set', function() { simulateInputContextFocusEvent('chocola'); p.then.getCall(0).args[0](undefined); sinon.assert.calledWithExactly( km._setKeyboardToShow, 'chocola', undefined ); }); test('Error should still trigger _setKeyboardToShow', function() { simulateInputContextFocusEvent('chocola'); p.mGetNextPromise().catch.getCall(0).args[0]('error'); sinon.assert.calledWithExactly(km._setKeyboardToShow, 'chocola'); }); }); }); suite('Try using the same layout when switching input types', function() { var oldInputLayouts; var oldlayoutToGroupMapping; setup(function() { oldInputLayouts = KeyboardManager.inputLayouts.layouts; oldlayoutToGroupMapping = KeyboardManager.inputLayouts._layoutToGroupMapping; KeyboardManager.inputLayouts.layouts = { text: [ { manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'en' }, { manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'fr' }, { manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'es' } ], password: [ { manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'en' }, { manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'fr' }, { manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'es' } ], number: [ { manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'number' } ] }; KeyboardManager.inputLayouts._layoutToGroupMapping = { 'app://keyboard.gaiamobile.org/manifest.webapp/en': [ { group: 'text', index: 0 }, { group: 'password', index: 0 } ], 'app://keyboard.gaiamobile.org/manifest.webapp/fr': [ { group: 'text', index: 1 }, { group: 'password', index: 1 } ], 'app://keyboard.gaiamobile.org/manifest.webapp/es': [ { group: 'text', index: 2 }, { group: 'password', index: 2 } ], 'app://keyboard.gaiamobile.org/manifest.webapp/number': [ { group: 'number', index: 0 } ] }; KeyboardManager.inputLayouts.layouts.text._activeLayoutIdx = 0; KeyboardManager.inputLayouts.layouts.password._activeLayoutIdx = 0; KeyboardManager.inputLayouts.layouts.number._activeLayoutIdx = 0; }); teardown(function() { KeyboardManager.inputLayouts.layouts = oldInputLayouts; KeyboardManager.inputLayouts._layoutToGroupMapping = oldlayoutToGroupMapping; }); test('change to text and to password', function() { KeyboardManager._setKeyboardToShow('text'); assert.isTrue( inputWindowManager.showInputWindow.calledWith({ manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'en' }), 'should change to first text layout' ); inputWindowManager.showInputWindow.reset(); KeyboardManager._switchToNext(); this.sinon.clock.tick(SWITCH_CHANGE_DELAY); assert.isTrue( inputWindowManager.showInputWindow.calledWith({ manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'fr' }), 'should change to second text layout' ); inputWindowManager.showInputWindow.reset(); KeyboardManager._setKeyboardToShow('password'); assert.isTrue( inputWindowManager.showInputWindow.calledWith({ manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'fr' }), 'should change to second password layout' ); }); test('change to text and to number and to password', function() { KeyboardManager._setKeyboardToShow('text'); assert.isTrue( inputWindowManager.showInputWindow.calledWith({ manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'en' }), 'should change to first text layout' ); inputWindowManager.showInputWindow.reset(); KeyboardManager._switchToNext(); this.sinon.clock.tick(SWITCH_CHANGE_DELAY); assert.isTrue( inputWindowManager.showInputWindow.calledWith({ manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'fr' }), 'should change to second text layout' ); inputWindowManager.showInputWindow.reset(); KeyboardManager._setKeyboardToShow('number'); inputWindowManager.showInputWindow.reset(); KeyboardManager._setKeyboardToShow('password'); assert.isTrue( inputWindowManager.showInputWindow.calledWith({ manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'fr' }), 'should change to second password layout' ); }); test('change to text, blur, and to password', function() { KeyboardManager._setKeyboardToShow('text'); assert.isTrue( inputWindowManager.showInputWindow.calledWith({ manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'en' }), 'should change to first text layout' ); inputWindowManager.showInputWindow.reset(); KeyboardManager._switchToNext(); this.sinon.clock.tick(SWITCH_CHANGE_DELAY); assert.isTrue( inputWindowManager.showInputWindow.calledWith({ manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'fr' }), 'should change to second text layout' ); simulateInputContextBlurEvent(); inputWindowManager.showInputWindow.reset(); KeyboardManager._setKeyboardToShow('password'); assert.isTrue( inputWindowManager.showInputWindow.calledWith({ manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'fr' }), 'should change to second password layout' ); }); }); suite('Fallback layouts', function() { var oldFallbackLayotus; setup(function() { oldFallbackLayotus = MockKeyboardHelper.fallbackLayouts; }); teardown(function() { MockKeyboardHelper.fallbackLayouts = oldFallbackLayotus; }); test('Should detect fallback and insert them', function() { MockKeyboardHelper.fallbackLayouts = { password: { app: { origin: 'app://keyboard.gaiamobile.org', manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp' }, layoutId: 'pwLayout', inputManifest: { launch_path: '/settings.html', name: 'pwInput' }, manifest: { name: 'pwInput' } } }; MockKeyboardHelper.watchCallback(KeyboardHelper.layouts, { apps: true }); assert.isTrue('password' in KeyboardManager.inputLayouts.layouts); assert.equal(KeyboardManager.inputLayouts.layouts.password[0].appName, 'pwInput'); assert.equal(KeyboardManager.inputLayouts.layouts.password[0].origin, 'app://keyboard.gaiamobile.org'); }); test('Should not insert extra fallback if already available', function() { MockKeyboardHelper.fallbackLayouts = { number: { app: { origin: 'app://app-number.gaiamobile.org', manifestURL: 'app://app-number.gaiamobile.org/manifest.webapp' }, layoutId: 'number2', inputManifest: { launch_path: '/settings.html', name: 'anotherNumberInput' }, manifest: { name: 'anotherNumberInput' } } }; MockKeyboardHelper.watchCallback(KeyboardHelper.layouts, { apps: true }); assert.isTrue(KeyboardManager.inputLayouts.layouts.number.every( layout => ('number2' !== layout.layoutId && 'pwInput' !== layout.appName && 'app://app-number.gaiamobile.org' !== layout.origin) )); }); }); test('_onKeyboardKilled', function() { var fakeFrame_A = { manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp', id: 'en'}; var _setKeyboardToShow = this.sinon.stub(KeyboardManager, '_setKeyboardToShow'); KeyboardManager._showingInputGroup = 'text'; KeyboardManager._onKeyboardKilled(fakeFrame_A.manifestURL); assert.ok(_setKeyboardToShow.calledWith('text')); }); suite('Event handler', function() { test('keyboardhide should reset _showingInputGroup', function() { var oldShowingInputGroup = KeyboardManager._showingInputGroup; KeyboardManager.handleEvent(new CustomEvent('keyboardhide')); assert.strictEqual(KeyboardManager._showingInputGroup, null); KeyboardManager._showingInputGroup = oldShowingInputGroup; }); }); suite('Showing & Preloading Keyboard', function() { test('setKeyboardToShow', function() { KeyboardManager._setKeyboardToShow('text', undefined); sinon.assert.called(inputWindowManager.showInputWindow); }); test('_onKeyboardReady', function() { this.sinon.stub(KeyboardManager, '_showIMESwitcher'); KeyboardManager._onKeyboardReady(); sinon.assert.called(KeyboardManager._showIMESwitcher); }); test('_preloadKeyboard', function() { KeyboardManager._preloadKeyboard(); assert.isTrue(inputWindowManager.preloadInputWindow.called); }); }); suite('Focus and Blur', function() { var imeSwitcherHide; setup(function() { // prevent _setKeyboardToShow callCount miscalculation // due to launch on boot inputWindowManager._inputWindows = { 'app://keyboard.gaiamobile.org/manifest.webapp': {} }; this.sinon.stub(KeyboardManager, '_setKeyboardToShow'); this.sinon.stub(KeyboardManager, '_showIMESwitcher'); imeSwitcherHide = this.sinon.stub(KeyboardManager.imeSwitcher, 'hide'); KeyboardManager.inputLayouts.layouts = { text: { _activeLayoutIdx: 0 } }; }); test('Blur should hide', function() { simulateInputContextBlurEvent(); sinon.assert.callCount(inputWindowManager.hideInputWindow, 1); sinon.assert.notCalled(KeyboardManager._setKeyboardToShow); sinon.assert.callCount(imeSwitcherHide, 1, 'IMESwitcher.hide should be called'); }); test('Focus should show', function() { simulateInputContextFocusEvent('text'); sinon.assert.notCalled(inputWindowManager.hideInputWindow); sinon.assert.callCount(KeyboardManager._setKeyboardToShow, 1); }); }); test('showIMESwitcher should call IMESwitcher.show properly', function() { var oldShowingInputGroup = KeyboardManager._showingInputGroup; var oldInputLayouts = KeyboardManager.inputLayouts.layouts; KeyboardManager._showingInputGroup = 'text'; KeyboardManager.inputLayouts.layouts = { text: [ { appName: 'DummyApp', name: 'DummyKB' } ] }; KeyboardManager.inputLayouts.layouts.text._activeLayoutIdx = 0; var stubIMESwitcherShow = this.sinon.stub(KeyboardManager.imeSwitcher, 'show'); KeyboardManager._showIMESwitcher(); sinon.assert.calledWith(stubIMESwitcherShow, 'DummyApp', 'DummyKB'); KeyboardManager._showingInputGroup = oldShowingInputGroup; KeyboardManager.inputLayouts.layouts = oldInputLayouts; }); suite('Switching keyboards within same type', function() { var oldInputLayouts; var oldShowingInputGroup; setup(function() { oldInputLayouts = KeyboardManager.inputLayouts.layouts; KeyboardManager.inputLayouts.layouts = { text: [ { name: 'English', appName: 'Built-in Keyboard', manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp' },{ name: 'French', appName: 'Built-out Keyboard', manifestURL: 'app://anotherkb.gaiamobile.org/manifest.webapp' },{ name: 'Chinese', appName: 'Built-inout Keyboard', manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp' } ] }; KeyboardManager.inputLayouts.layouts.text._activeLayoutIdx = 2; oldShowingInputGroup = KeyboardManager._showingInputGroup; KeyboardManager._showingInputGroup = 'text'; }); teardown(function() { KeyboardManager._showingInputGroup = oldShowingInputGroup; KeyboardManager.inputLayouts.layouts = oldInputLayouts; }); test('showImeMenu / call to ImeMenu', function(){ var oldMozL10n; var stubWaitForSwitchTimeout; var stubImeMenuCallback; oldMozL10n = navigator.mozL10n; navigator.mozL10n = MockL10n; stubWaitForSwitchTimeout = this.sinon.stub(KeyboardManager, '_waitForSwitchTimeout'); stubImeMenuCallback = this.sinon.stub(KeyboardManager, '_imeMenuCallback'); MockImeMenu.mSetup(); KeyboardManager._showImeMenu(); stubWaitForSwitchTimeout.getCall(0).args[0](); assert.isTrue(inputWindowManager.hideInputWindow.called); var imeMenu = MockImeMenu.instances[0]; assert.deepEqual(imeMenu.listItems, [{ name: 'English', nameL10nId: undefined, appName: 'Built-in Keyboard', value: 0, selected: false }, { name: 'French', nameL10nId: undefined, appName: 'Built-out Keyboard', value: 1, selected: false }, { name: 'Chinese', nameL10nId: undefined, appName: 'Built-inout Keyboard', value: 2, selected: true }]); imeMenu.onselected(); imeMenu.oncancel(); assert.isTrue(stubImeMenuCallback.alwaysCalledWith('text')); assert.equal(stubImeMenuCallback.callCount, 2); assert.equal(imeMenu.title, 'choose-option'); imeMenu.mTeardown(); navigator.mozL10n = oldMozL10n; }); suite('imeMenuCallback', function() { var stubSetKeyboardToShow; var stubDispatchEvent; setup(function() { stubSetKeyboardToShow = this.sinon.stub(KeyboardManager, '_setKeyboardToShow'); stubDispatchEvent = this.sinon.stub(window, 'dispatchEvent'); }); test('success', function(){ KeyboardManager._imeMenuCallback('text', 1); assert.isTrue(stubSetKeyboardToShow.calledWith('text', 1)); assert.equal(stubDispatchEvent.getCall(0).args[0].type, 'keyboardchanged'); }); test('cancel', function(){ KeyboardManager._imeMenuCallback('text'); assert.isTrue(stubSetKeyboardToShow.calledWithExactly('text')); assert.equal(stubDispatchEvent.getCall(0).args[0].type, 'keyboardchangecanceled'); }); }); test('switchToNext', function() { var oldShowingInputGroup = KeyboardManager._showingInputGroup; KeyboardManager._showingInputGroup = 'text'; KeyboardManager.inputLayouts.layouts.text._activeLayoutIdx = 2; var stubWaitForSwitchTimeout = this.sinon.stub(KeyboardManager, '_waitForSwitchTimeout'); var stubSetKeyboardToShow = this.sinon.stub(KeyboardManager, '_setKeyboardToShow'); KeyboardManager._switchToNext(); stubWaitForSwitchTimeout.getCall(0).args[0](); assert.isTrue(stubSetKeyboardToShow.calledWith('text', 0)); KeyboardManager._showingInputGroup = oldShowingInputGroup; }); test('waitForSwitchTimeout helper', function(done) { var oldSwitchChangeTimeout = KeyboardManager._switchChangeTimeout; KeyboardManager._switchChangeTimeout = 1234; var stubClearTimeout = this.sinon.stub(window, 'clearTimeout'); KeyboardManager._waitForSwitchTimeout(function(){ done(); }); assert.isTrue(stubClearTimeout.calledWith(1234)); this.sinon.clock.tick(SWITCH_CHANGE_DELAY); KeyboardManager._switchChangeTimeout = oldSwitchChangeTimeout; }); }); suite('Switching keyboard layout', function() { var _layouts; suiteSetup(function() { _layouts = KeyboardManager.inputLayouts.layouts; }); suiteTeardown(function() { KeyboardManager.inputLayouts.layouts = _layouts; }); setup(function() { KeyboardManager.inputLayouts.layouts = { 'text': [ { id: 'fk', manifestURL: 'app://fake/manifest.webapp' }, { id: 'ur', manifestURL: 'app://unreal/manifest.webapp' } ] }; KeyboardManager._showingInputGroup = 'text'; }); test('Switching stores new layout in settings', function() { KeyboardManager.inputLayouts.layouts.text._activeLayoutIdx = 0; KeyboardManager.inputLayouts. _layoutToGroupMapping['app://unreal/manifest.webapp/ur'] = [{ group: 'text', index: 3 }]; var stubSaveGroupsCurrentActiveLayout = this.sinon.stub(InputLayouts.prototype, 'saveGroupsCurrentActiveLayout'); KeyboardManager._switchToNext(); this.sinon.clock.tick(SWITCH_CHANGE_DELAY); assert.isTrue(stubSaveGroupsCurrentActiveLayout.calledOnce); assert.isTrue( stubSaveGroupsCurrentActiveLayout.calledWith( {id: 'ur', manifestURL: 'app://unreal/manifest.webapp'} ) ); stubSaveGroupsCurrentActiveLayout.restore(); }); test('Switching calls setKeyboardToShow', function() { KeyboardManager._setKeyboardToShow = this.sinon.stub(); KeyboardManager.inputLayouts.layouts.text._activeLayoutIdx = 1; KeyboardManager._switchToNext(); this.sinon.clock.tick(SWITCH_CHANGE_DELAY); sinon.assert.callCount(KeyboardManager._setKeyboardToShow, 1); sinon.assert.calledWith(KeyboardManager._setKeyboardToShow, 'text', 0); }); }); suite('updateLayouts', function() { var oldShowingInputGroup; var stubProcessLayouts; setup(function(){ oldShowingInputGroup = KeyboardManager._showingInputGroup; }); teardown(function(){ KeyboardManager._showingInputGroup = oldShowingInputGroup; }); test('updateLayouts calls functions as needed', function() { KeyboardManager._showingInputGroup = 'text'; stubProcessLayouts = this.sinon.stub(KeyboardManager.inputLayouts, 'processLayouts'); stubProcessLayouts.returns( new Set(['app://keyboard1.gaiamobila.org/manifest.webapp', 'app://keyboard2.gaiamobila.org/manifest.webapp'])); inputWindowManager.getLoadedManifestURLs.returns([ 'app://keyboard1.gaiamobila.org/manifest.webapp', 'app://keyboard2.gaiamobila.org/manifest.webapp', 'app://keyboard3.gaiamobila.org/manifest.webapp', 'app://keyboard4.gaiamobila.org/manifest.webapp' ]); KeyboardManager._tryLaunchOnBoot.reset(); var layouts = [ { app: { manifestURL: 'app://keyboard1.gaiamobila.org/manifest.webapp' }, }, { app: { manifestURL: 'app://keyboard2.gaiamobila.org/manifest.webapp' } } ]; KeyboardManager._updateLayouts(layouts); assert.isTrue(stubProcessLayouts.calledWith(layouts)); // updateLayouts is always called at KeyboardManager.start() // so we need to check against the last call of updateLayouts var lastCallIndex = inputWindowManager._onInputLayoutsRemoved.callCount - 1; assert.deepEqual( inputWindowManager._onInputLayoutsRemoved.getCall(lastCallIndex) .args[0], ['app://keyboard3.gaiamobila.org/manifest.webapp', 'app://keyboard4.gaiamobila.org/manifest.webapp'], 'kb3 and kb4 should be removed' ); assert.isTrue(KeyboardManager._tryLaunchOnBoot.called); }); test('reset showingInputGroup as needed', function() { stubProcessLayouts.returns(new Set([])); inputWindowManager.getLoadedManifestURLs.returns([]); KeyboardManager._showingInputGroup = 'number'; inputWindowManager._onInputLayoutsRemoved.returns(false); KeyboardManager._updateLayouts([]); assert.equal(KeyboardManager._showingInputGroup, 'number', `showingInputGroup should not be reset when current layout is not removed`); inputWindowManager._onInputLayoutsRemoved.returns(true); KeyboardManager._updateLayouts([]); assert.strictEqual(KeyboardManager._showingInputGroup, null, `showingInputGroup should be reset when current layout is removed`); }); }); });