diff --git a/apps/communications/dialer/test/unit/keypad_test.js b/apps/communications/dialer/test/unit/keypad_test.js index ae74d2e0f525..5b12cd720b38 100644 --- a/apps/communications/dialer/test/unit/keypad_test.js +++ b/apps/communications/dialer/test/unit/keypad_test.js @@ -66,43 +66,50 @@ suite('dialer/keypad', function() { mocksHelperForKeypad.attachTestHelpers(); - // Helpers for testing abbreviated dialing codes - var node; - var fakeEventStart; - var fakeEventEnd; + // Dummy node used as event target + var dummyNode = document.createElement('div'); /** - * Create the mock elements needed to test abbreviated dialing codes. + * Simulate a touchstart event + * + * @param key {String} The target's dataset value, the touched key */ - function setupAbbreviatedDialingCodesMocks() { - subject._phoneNumber = ''; - node = document.createElement('div'); - fakeEventStart = { - target: node, + function mockTouchStart(key) { + var fakeEvent = { + target: dummyNode, preventDefault: function() {}, stopPropagation: function() {}, type: 'touchstart' }; - fakeEventEnd = { - target: node, + dummyNode.dataset.value = key; + subject.keyHandler(fakeEvent); + } + + /** + * Simulate a touchend event + * + * @param key {String} The target's dataset value, the touched key + */ + function mockTouchEnd(key) { + var fakeEvent = { + target: dummyNode, preventDefault: function() {}, stopPropagation: function() {}, type: 'touchend' }; + dummyNode.dataset.value = key; + subject.keyHandler(fakeEvent); } /** - * Send the fake events needed to emulate the typing of an abbreviated - * dialing code. + * Send the fake events needed to emulate the typing of an umber. * - * @param {String} number The abbrevited dialing code to type. + * @param {String} number The number to type. */ - function typeAbbreviatedDialingCode(number) { + function mockTypeNumber(number) { for (var i = 0, end = number.length; i < end; i++) { - fakeEventStart.target.dataset.value = number.charAt(i); - subject.keyHandler(fakeEventStart); - fakeEventEnd.target.dataset.value = number.charAt(i); - subject.keyHandler(fakeEventEnd); + mockTouchStart(number[i]); + mockTouchEnd(number[i]); } } @@ -139,9 +146,12 @@ suite('dialer/keypad', function() { setup(function() { this.sinon.useFakeTimers(); + dummyNode = document.createElement('div'); + subject._phoneNumber = ''; }); teardown(function() { + dummyNode = null; MockNavigatorMozTelephony.mTeardown(); }); @@ -186,19 +196,13 @@ suite('dialer/keypad', function() { var mmi = '*#06#'; var speedDialNum = '1#'; - setup(function() { - setupAbbreviatedDialingCodesMocks(); - }); - test('Properly highlight the last key in an abbreviated dialing code', function() { for (var i = 0, end = mmi.length; i < end; i++) { - fakeEventStart.target.dataset.value = mmi.charAt(i); - subject.keyHandler(fakeEventStart); - assert.isTrue(node.classList.contains('active')); - fakeEventEnd.target.dataset.value = mmi.charAt(i); - subject.keyHandler(fakeEventEnd); - assert.isFalse(node.classList.contains('active')); + mockTouchStart(mmi[i]); + assert.isTrue(dummyNode.classList.contains('active')); + mockTouchEnd(mmi[i]); + assert.isFalse(dummyNode.classList.contains('active')); } }); @@ -210,11 +214,8 @@ suite('dialer/keypad', function() { * moving the cursor to insert the #. In this scenario an abbreviated * dialing operation should not be triggered. */ subject._phoneNumber = '1#1'; - - fakeEventStart.target.dataset.value = 'delete'; - subject.keyHandler(fakeEventStart); - fakeEventEnd.target.dataset.value = 'delete'; - subject.keyHandler(fakeEventEnd); + mockTouchStart('delete'); + mockTouchEnd('delete'); sinon.assert.notCalled(KeypadManager._getSpeedDialNumber); }); @@ -222,7 +223,7 @@ suite('dialer/keypad', function() { test('Get IMEI via send MMI', function() { this.sinon.spy(MockMultiSimActionButtonSingleton, 'performAction'); - typeAbbreviatedDialingCode(mmi); + mockTypeNumber(mmi); sinon.assert.calledOnce( MockMultiSimActionButtonSingleton.performAction); @@ -233,14 +234,24 @@ suite('dialer/keypad', function() { return Promise.resolve('123'); }); - typeAbbreviatedDialingCode(speedDialNum); + mockTypeNumber(speedDialNum); sinon.assert.calledWith(KeypadManager._getSpeedDialNumber, 1); }); }); + test('Multi-tap events are ignored', function() { + mockTouchStart('1'); + assert.equal(subject._phoneNumber, '1'); + mockTouchStart('2'); + assert.equal(subject._phoneNumber, '1'); + mockTouchEnd('2'); + assert.equal(subject._phoneNumber, '1'); + mockTouchEnd('1'); + assert.equal(subject._phoneNumber, '1'); + }); + test('Call button pressed with no calls in Call Log', function() { - subject._phoneNumber = ''; subject.fetchLastCalled(); assert.equal(subject._phoneNumber, ''); }); @@ -254,7 +265,6 @@ suite('dialer/keypad', function() { status: 'connected' }; CallLogDBManager.add(recentCall, function(result) { - subject._phoneNumber = ''; subject.fetchLastCalled(); assert.equal(subject._phoneNumber, ''); }); @@ -266,7 +276,6 @@ suite('dialer/keypad', function() { type: 'dialing', date: Date.now() }; - subject._phoneNumber = ''; CallLogDBManager.add(recentCall); subject.fetchLastCalled(); assert.equal(subject._phoneNumber, recentCall.number); @@ -274,50 +283,18 @@ suite('dialer/keypad', function() { test('Dialer is limited to 50 digits', function() { var digits = '111111111122222222223333333333444444444455555555556'; - var fakeEvent = { - target: { - dataset: { - value: null - }, - classList: { - add: function() {}, - remove: function() {} - } - }, - preventDefault: function() {}, - stopPropagation: function() {}, - type: null - }; - subject._phoneNumber = ''; - for (var i = 0, end = digits.length; i < end; i++) { - fakeEvent.target.dataset.value = digits.charAt(i); - fakeEvent.type = 'touchstart'; - subject.keyHandler(fakeEvent); - fakeEvent.type = 'touchend'; - subject.keyHandler(fakeEvent); - } + mockTypeNumber(digits); + assert.equal(subject._phoneNumber, digits.substring(0, 50)); }); test('Adds active class to keys when pressed', function() { - var fakeEvent = { - target: document.createElement('div'), - preventDefault: function() {}, - stopPropagation: function() {}, - type: null - }; - fakeEvent.target.dataset.value = 1; - - subject._phoneNumber = ''; - - assert.isFalse(fakeEvent.target.classList.contains('active')); - fakeEvent.type = 'touchstart'; - subject.keyHandler(fakeEvent); - assert.isTrue(fakeEvent.target.classList.contains('active')); - fakeEvent.type = 'touchend'; - subject.keyHandler(fakeEvent); - assert.isFalse(fakeEvent.target.classList.contains('active')); + assert.isFalse(dummyNode.classList.contains('active')); + mockTouchStart('1'); + assert.isTrue(dummyNode.classList.contains('active')); + mockTouchEnd('1'); + assert.isFalse(dummyNode.classList.contains('active')); }); test('FontSizeManager is invoked with the right parameters', function() { @@ -424,8 +401,7 @@ suite('dialer/keypad', function() { test('Disable abbreviated dialing codes', function() { this.sinon.spy(KeypadManager, '_getSpeedDialNumber'); - setupAbbreviatedDialingCodesMocks(); - typeAbbreviatedDialingCode('1#'); + mockTypeNumber('1#'); sinon.assert.notCalled(KeypadManager._getSpeedDialNumber); }); @@ -520,29 +496,8 @@ suite('dialer/keypad', function() { test('Dialer is not limited to 50 digits while on a call', function() { var digits = '11111111112222222222333333333344444444445555555555' + '6666666666'; - var fakeEvent = { - target: { - dataset: { - value: null - }, - classList: { - add: function() {}, - remove: function() {} - } - }, - preventDefault: function() {}, - stopPropagation: function() {}, - type: null - }; - subject._phoneNumber = ''; - for (var i = 0, end = digits.length; i < end; i++) { - fakeEvent.target.dataset.value = digits.charAt(i); - fakeEvent.type = 'touchstart'; - subject.keyHandler(fakeEvent); - fakeEvent.type = 'touchend'; - subject.keyHandler(fakeEvent); - } + mockTypeNumber(digits); assert.equal(subject._phoneNumber, digits); }); @@ -749,10 +704,6 @@ suite('dialer/keypad', function() { suite('Speed dial', function() { var speedDialNum = '1#'; - setup(function() { - subject._phoneNumber = ''; - }); - test(speedDialNum + ' is a speed dial number', function() { assert.isTrue(subject._isSpeedDialNumber(speedDialNum)); }); diff --git a/shared/js/dialer/keypad.js b/shared/js/dialer/keypad.js index 8e6ed6002d80..42c842ad92d7 100644 --- a/shared/js/dialer/keypad.js +++ b/shared/js/dialer/keypad.js @@ -312,6 +312,14 @@ var KeypadManager = { this._longPress = false; this._lastPressedKey = key; + // If user input number more 50 digits, app shouldn't accept. + // The limit only applies while not on a call - there is no + // limit while on a call (bug 917630). + if (key !== 'delete' && !this._onCall && + (this._phoneNumber.length >= this.kMaxDigits)) { + return; + } + if (key != 'delete') { if (this._keypadSoundIsEnabled) { // We do not support long press if not on a call @@ -379,6 +387,20 @@ var KeypadManager = { this._insertAtCaret(key); } + // Per certification requirements abbreviated dialing codes need to be + // called immediately after the user enters the '#' key. This covers + // retrieving the device's IMEI codes as well as speed dialing. + if (key === '#' && !this._onCall) { + if (this._phoneNumber === '*#06#') { + this.multiSimActionButton.performAction(); + return; + } else if (this._isSpeedDialNumber(this._phoneNumber)) { + // Remove the trailing '#' to get the index + this._handleSpeedDial(this._phoneNumber.slice(0, -1)); + return; + } + } + setTimeout(function(self) { self._updatePhoneNumberView('begin', false); }, 0, this); @@ -397,7 +419,6 @@ var KeypadManager = { if (key !== this._lastPressedKey || key === 'delete') { this._stopDtmfTone(); - this._lastPressedKey = null; } }, @@ -408,9 +429,10 @@ var KeypadManager = { * @param {String} key The key over which the tap finished. */ _touchEnd: function kh_touchEnd(key) { - if (key !== 'delete' && key === this._lastPressedKey) { + this._lastPressedKey = null; + + if (key !== 'delete') { this._stopDtmfTone(); - this._lastPressedKey = null; } if (this._keypadSoundIsEnabled) { @@ -452,51 +474,14 @@ var KeypadManager = { return; } - // Per certification requirements abbreviated dialing codes need to be - // called immediately after the user enters the '#' key. This covers - // retrieving the device's IMEI codes as well as speed dialing. - if (key === '#' && !this._onCall) { - if (this._phoneNumber === '*#06#') { - this.multiSimActionButton.performAction(); - event.target.classList.remove('active'); - return; - } else if (this._isSpeedDialNumber(this._phoneNumber)) { - var self = this; - var index = this._phoneNumber.slice(0, -1); // Remove the trailing '#' - - this.updatePhoneNumber('', 'begin', false); - this._getSpeedDialNumber(+index).then( - function(number) { - self.updatePhoneNumber(number, 'begin', false); - }, function(error) { - /* Do not display an error message if the user explicitly - * cancelled the speed dial operation. */ - if (error) { - ConfirmDialog.show(error, null, { - title: 'noContactsFoundDialogOk', - callback: ConfirmDialog.hide - }); - } - }); - - event.target.classList.remove('active'); - return; - } - } - - // If user input number more 50 digits, app shouldn't accept. - // The limit only applies while not on a call - there is no - // limit while on a call (bug 917630). - if (key != 'delete' && this._phoneNumber.length >= this.kMaxDigits && - !this._onCall) { - event.target.classList.remove('active'); - return; - } - event.stopPropagation(); switch (event.type) { case 'touchstart': + if (this._lastPressedKey) { + return; // Ignore multi-taps + } + event.target.classList.add('active'); this._touchStart(key); break; @@ -505,6 +490,10 @@ var KeypadManager = { break; case 'touchend': case 'touchcancel': + if (key !== this._lastPressedKey) { + return; // Ignore multi-taps + } + event.target.classList.remove('active'); this._touchEnd(key); break; @@ -566,6 +555,31 @@ var KeypadManager = { }); }, + /** + * Handles a speed dial number, replaces the phone number with the one + * at the specified index or displays an error message if none was found. + * + * @param {Integer} index The index of the speed dial number. + */ + _handleSpeedDial: function(index) { + var self = this; + + this.updatePhoneNumber('', 'begin', false); + this._getSpeedDialNumber(+index).then( + function(number) { + self.updatePhoneNumber(number, 'begin', false); + }, function(error) { + /* Do not display an error message if the user explicitly + * cancelled the speed dial operation. */ + if (error) { + ConfirmDialog.show(error, null, { + title: 'noContactsFoundDialogOk', + callback: ConfirmDialog.hide + }); + } + }); + }, + /** * Creates an array of contacts populated using the ADN contacts retrieved * from a SIM card. Every contact will contain only the ID and first