Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,18 @@
{
"extends": "../../../../../.jshintrc",
"predef": [
"suite",
"test",
"setup",
"teardown",
"suiteSetup",
"suiteTeardown",
"marionette",
"marionetteScriptFinished",
"module",
"require",
"define",
"requireApp",
"sinon"
]
}

View file

@ -0,0 +1,50 @@
'use strict';
var Dialer = require('./lib/dialer');
var CallLogEntriesManager = require('./fake/call_log_entries_manager.js');
marionette('Dialer > Call log', function() {
var client = marionette.client(Dialer.config);
var dialer;
var callLog;
setup(function() {
dialer = new Dialer(client);
dialer.launch();
client.loader.getMockManager('dialer').inject('navigator_moz_icc_manager');
var entriesManager = new CallLogEntriesManager(dialer);
var partialEntries = [
{},
{'number': '123'},
{'number': '456'}
];
entriesManager.generateAndAdd(partialEntries);
callLog = dialer.tabs.goToCallLog(partialEntries.length);
});
test('Entering the edit mode hides the filters', function() {
callLog.enterEditMode(); // Hide verification is done there
});
suite('Edit mode should be hidden', function() {
setup(function() {
callLog.enterEditMode();
});
test('when exiting it with cancel', function() {
callLog.exitEditMode(); // Hide verification is done there
});
test('when deleting some (but not all) call log entries', function() {
callLog.deleteEntries([0, 1]); // Hide verification is done there
});
test('when deleting all call log entries', function() {
callLog.deleteAllEntries(); // Hide verification is done there
});
});
});

View file

@ -0,0 +1,41 @@
'use strict';
var _ = require('lodash');
var DEFAULT_ENTRY = {
'date': Date.now(),
'type': 'incoming',
'number': '5551234567',
'serviceId': 0,
'emergency': false,
'voicemail': false,
'status': 'connected'
};
function CallLogEntries(dialer) {
this.dialer = dialer;
this.client = dialer.client;
}
CallLogEntries.prototype = {
generateAndAdd: function(entriesParameters) {
entriesParameters = Array.isArray(entriesParameters) ?
entriesParameters : [entriesParameters];
entriesParameters.forEach(function(entryParameters) {
this._generateOneAndAdd(entryParameters);
}, this);
this.dialer.relaunch();
},
_generateOneAndAdd: function(parameters) {
var entry = _.assign({}, DEFAULT_ENTRY, parameters);
this.client.executeScript(function(entry) {
window.wrappedJSObject.CallLogDBManager.add(entry);
}, [entry]);
}
};
module.exports = CallLogEntries;

View file

@ -0,0 +1,332 @@
'use strict';
var Dialer = require('./lib/dialer');
var ReflowHelper =
require('../../../../../tests/jsmarionette/plugins/reflow_helper.js');
marionette('Dialer > Keypad', function() {
var assert = require('assert');
var client = marionette.client(Dialer.config);
var subject;
var selectors;
var actions;
var reflowHelper;
setup(function() {
actions = client.loader.getActions();
subject = new Dialer(client);
subject.launch();
selectors = Dialer.Selectors;
var keypad = subject.client.findElement(selectors.keypadView);
client.waitFor(function() {
return keypad.displayed();
});
reflowHelper = new ReflowHelper(client);
});
function typeNumber() {
var one = subject.client.findElement(selectors.one);
keypadTap(one);
var two = subject.client.findElement(selectors.two);
keypadTap(two);
var three = subject.client.findElement(selectors.three);
keypadTap(three);
}
// Taps digits in 1, 2, 3 sequence until producing an ellipsis. We make this
// instead of entering fixed number as the digit at which the ellipsis
// appears depends on the integration environment.
function typeLongNumber() {
var number = subject.client.findElement(selectors.phoneNumber);
var numberTyped = '';
var one = subject.client.findElement(selectors.one);
var two = subject.client.findElement(selectors.two);
var three = subject.client.findElement(selectors.three);
var sequence = [one, two, three];
var length = sequence.length;
for (var i = 0, digit; (digit = sequence[i]); i = (i+1) % length) {
keypadTap(digit);
numberTyped += digit.getAttribute('data-value');
if (number.getAttribute('value').indexOf('\u2026') === 0) {
return numberTyped;
}
}
}
// Avoid "all the same number" patterns like 111111... because, under
// ellipsis scenaries, the value of the input won't change and the test
// will timeout.
function keypadTap(elem, longPress) {
var number = subject.client.findElement(selectors.phoneNumber);
var previousValue = number.getAttribute('value');
if (longPress) {
actions.longPress(elem, 1).perform();
} else {
actions.tap(elem).perform();
}
client.waitFor(function () {
return number.getAttribute('value') !== previousValue;
});
}
function loadSuggestionDOM() {
typeNumber();
var del = subject.client.findElement(selectors.del);
actions.longPress(del, 1).perform();
var number = subject.client.findElement(selectors.phoneNumber);
client.waitFor(function() {
return (number.getAttribute('value') === '');
});
}
test('Add a contact from dialer', function() {
subject.keypadView.typePhoneNumber(12345);
var addToContactsActivities = subject.keypadView.addToContacts();
var newContact = addToContactsActivities.pickCreateNew();
var contactName = 'Name';
var dialer = newContact.createNewContactAndReturnToDialer(contactName);
var contactList = dialer.tabs.goToContactList();
var contactCreated = contactList.getContactFirstName();
assert.strictEqual(contactCreated, contactName);
});
test('Entering a 3 digits number with the keypad', function() {
loadSuggestionDOM();
reflowHelper.startTracking(Dialer.URL + '/manifest.webapp');
typeNumber();
var number = subject.client.findElement(selectors.phoneNumber);
assert.equal(number.getAttribute('value'), '123');
var reflowCount = reflowHelper.getCount();
assert.equal(reflowCount, 0);
reflowHelper.stopTracking();
});
test('Entering a digit in the middle of the number', function() {
typeNumber();
var number = subject.client.findElement(selectors.phoneNumber);
number.scriptWith(function (numberElement) {
numberElement.selectionStart = numberElement.selectionEnd = 1;
numberElement.click();
});
var zero = subject.client.findElement(selectors.zero);
keypadTap(zero);
assert.equal(number.getAttribute('value'), '1023');
});
test('Replace a selection in the middle of the number', function() {
typeNumber();
var number = subject.client.findElement(selectors.phoneNumber);
number.scriptWith(function (numberElement) {
numberElement.selectionStart = 1;
numberElement.selectionEnd = 2;
numberElement.click();
});
var zero = subject.client.findElement(selectors.zero);
keypadTap(zero);
assert.equal(number.getAttribute('value'), '103');
});
test('Replace a selection in the middle of the number with a long press',
function() {
typeNumber();
var number = subject.client.findElement(selectors.phoneNumber);
number.scriptWith(function (numberElement) {
numberElement.selectionStart = 1;
numberElement.selectionEnd = 2;
numberElement.click();
});
var zero = subject.client.findElement(selectors.zero);
keypadTap(zero, true);
assert.equal(number.getAttribute('value'), '1+3');
});
test('Entering a long press in the middle of the number', function() {
typeNumber();
var number = subject.client.findElement(selectors.phoneNumber);
number.scriptWith(function (numberElement) {
numberElement.selectionStart = numberElement.selectionEnd = 1;
numberElement.click();
});
var zero = subject.client.findElement(selectors.zero);
keypadTap(zero, true);
assert.equal(number.getAttribute('value'), '1+23');
});
test('Entering a digit in the middle of a long number', function() {
var insPosition = 3; // index where the cursor would be placed by the user
var realInsPosition = insPosition + 2; // index inside the complete number
// +2 for the space of the ellipsis
var typedNumber = typeLongNumber();
var numberAfterInsertion =
typedNumber.substr(0, realInsPosition) + '0' +
typedNumber.substr(realInsPosition);
var expectedValueAfterInsertion =
'\u2026' + numberAfterInsertion.substr(4); // 1 for the overflowing char,
// 2 for the ellipsis itself,
// 1 more for the new char.
var number = subject.client.findElement(selectors.phoneNumber);
var args = [number, insPosition];
client.executeScript(function (numberElement, insPosition) {
numberElement.selectionStart = numberElement.selectionEnd = insPosition;
numberElement.click();
}, args);
var zero = subject.client.findElement(selectors.zero);
keypadTap(zero);
assert.equal(number.getAttribute('value'), expectedValueAfterInsertion);
});
test('Entering a long press in the middle of a long number', function() {
var insPosition = 3; // index where the cursor would be placed by the user
var realInsPosition = insPosition + 2; // index inside the complete number
// +2 for the space of the ellipsis
var typedNumber = typeLongNumber();
var numberAfterInsertion =
typedNumber.substr(0, realInsPosition) + '+' +
typedNumber.substr(realInsPosition);
var expectedValueAfterInsertion =
'\u2026' + numberAfterInsertion.substr(4); // 1 for the overflowing char,
// 2 for the ellipsis itself,
// 1 more for the new char.
var number = subject.client.findElement(selectors.phoneNumber);
var args = [number, insPosition];
client.executeScript(function (numberElement, insPosition) {
numberElement.selectionStart = numberElement.selectionEnd = insPosition;
numberElement.click();
}, args);
var zero = subject.client.findElement(selectors.zero);
keypadTap(zero, true);
assert.equal(number.getAttribute('value'), expectedValueAfterInsertion);
});
test('Using the special extention key', function() {
var zero = subject.client.findElement(selectors.zero);
var number = subject.client.findElement(selectors.phoneNumber);
keypadTap(zero, true);
assert.equal(number.getAttribute('value'), '+');
keypadTap(zero, false);
assert.equal(number.getAttribute('value'), '+0');
});
test('Deleting a digit', function() {
typeNumber();
var del = subject.client.findElement(selectors.del);
actions.tap(del).perform();
var number = subject.client.findElement(selectors.phoneNumber);
client.waitFor(function() {
return (number.getAttribute('value').length == 2);
});
assert.equal(number.getAttribute('value'), '12');
});
test('Deleting a digit in the middle of the number', function() {
typeNumber();
var number = subject.client.findElement(selectors.phoneNumber);
number.scriptWith(function (numberElement) {
numberElement.selectionStart = numberElement.selectionEnd = 2;
numberElement.click();
});
var del = subject.client.findElement(selectors.del);
actions.tap(del).perform();
client.waitFor(function() {
return (number.getAttribute('value').length == 2);
});
assert.equal(number.getAttribute('value'), '13');
});
test('Deleting a digit in the middle of a long number', function() {
var delPosition = 3; // index where the cursor would be placed by the user
var realDelPosition = delPosition + 2; // index inside the complete number
// +2 for the space of the ellipsis
var typedNumber = typeLongNumber();
var expectedNumberAfterDeletion =
typedNumber.substr(0, realDelPosition - 1) +
typedNumber.substr(realDelPosition);
var number = subject.client.findElement(selectors.phoneNumber);
var args = [number, delPosition];
client.executeScript(function (numberElement, delPosition) {
numberElement.selectionStart = numberElement.selectionEnd = delPosition;
numberElement.click();
}, args);
var del = subject.client.findElement(selectors.del);
actions.tap(del).perform();
client.waitFor(function() {
return (number.getAttribute('value') == expectedNumberAfterDeletion);
});
assert.ok(true);
});
test('Deleting a couple of digits in the middle of the number', function() {
typeNumber();
var number = subject.client.findElement(selectors.phoneNumber);
number.scriptWith(function (numberElement) {
numberElement.selectionStart = numberElement.selectionEnd = 2;
numberElement.click();
});
var del = subject.client.findElement(selectors.del);
actions.doubleTap(del).perform();
assert.equal(number.getAttribute('value'), '3');
});
test('Clearing the number partially by long pressing the delete key',
function() {
typeNumber();
var number = subject.client.findElement(selectors.phoneNumber);
number.scriptWith(function (numberElement) {
numberElement.selectionStart = numberElement.selectionEnd = 2;
numberElement.click();
});
var del = subject.client.findElement(selectors.del);
actions.longPress(del, 1).perform();
client.waitFor(function() {
return (number.getAttribute('value') === '3');
});
assert.ok(true, 'cleaned the phone number view');
});
});

View file

@ -0,0 +1,90 @@
'use strict';
/* global module */
var KeypadView = require('./views/keypad/views.js');
var TabsView = require('./views/tabs/view.js');
/**
* Abstraction around dialer app.
* @constructor
* @param {Marionette.Client} client for operations.
*/
function Dialer(client) {
this.client = client;
}
/**
* @type String Origin of dialer app
*/
Dialer.URL = 'app://communications.gaiamobile.org';
Dialer.config = {
profile: {
settings: {
'devtools.overlay': true,
'hud.reflows': true
},
prefs: {
'devtools.debugger.forbid-certified-apps': false
}
},
desiredCapabilities: {
'raisesAccessibilityExceptions': false
}
};
Dialer.Selectors = {
phoneNumber: '#phone-number-view',
keypadView: '#keyboard-view',
del: '#keypad-delete',
zero: '.keypad-key[data-value="0"]',
one: '.keypad-key[data-value="1"]',
two: '.keypad-key[data-value="2"]',
three: '.keypad-key[data-value="3"]',
keypadCallBarAddContact: '#keypad-callbar-add-contact',
contactsTabItem: '#option-contacts',
callLogTabItem: '#option-recents',
callLogTabs: '#call-log-filter',
callLogEditForm: '#edit-mode',
addToExistingContactMenuItem: 'button[data-l10n-id="addToExistingContact"]'
};
Dialer.prototype = {
/**
* Launches dialer app and focuses on frame.
*/
launch: function() {
this.client.apps.launch(Dialer.URL, 'dialer');
this.switchTo();
this.client.helper.waitForElement('body');
},
relaunch: function() {
this.client.apps.close(Dialer.URL, 'dialer');
this.launch();
},
switchTo: function() {
this.client.switchToFrame();
// switchToApp already waits for the app to be displayed
this.client.apps.switchToApp(Dialer.URL, 'dialer');
},
get phoneNumber() {
return this.client.helper.waitForElement(Dialer.Selectors.phoneNumber)
.getAttribute('value');
},
get keypadView() {
return new KeypadView(this.client);
},
get tabs() {
return new TabsView(this.client);
}
};
module.exports = Dialer;

View file

@ -0,0 +1,18 @@
'use strict';
/* global module */
var SELECTORS = Object.freeze({
addNewContact: 'button[data-l10n-id="createNewContact"]',
});
function ActivitiesAccessor(client) {
this.client = client;
}
ActivitiesAccessor.prototype = {
get addNewContactMenuItem() {
return this.client.helper.waitForElement(SELECTORS.addNewContact);
}
};
module.exports = ActivitiesAccessor;

View file

@ -0,0 +1,25 @@
'use strict';
/* global module */
var ActivitiesAccessor = require('./accessors');
function ActivitiesView(client) {
this.client = client;
this.accessors = new ActivitiesAccessor(client);
}
ActivitiesView.prototype = {
pickCreateNew: function() {
this.client.waitFor(function() {
return this.accessors.addNewContactMenuItem.enabled();
}.bind(this));
var button = this.accessors.addNewContactMenuItem;
button.tap();
var contacts = this.client.loader.getAppClass('contacts');
contacts.switchToCreateNewContactActivity();
return contacts;
}
};
module.exports = ActivitiesView;

View file

@ -0,0 +1,77 @@
'use strict';
var SELECTORS = Object.freeze({
editButton: '#call-log-icon-edit',
callLogTabs: '#call-log-filter',
callLogItem: '.log-item',
callLogNoResults: '#no-result-message',
editHeader: '#edit-mode-header',
selectAllButton: '#select-all-threads',
deleteButton: '#delete-button',
confirmDeleteButton: '#confirmation-message [data-l10n-id="delete"]'
});
function CallLogAccessors(client) {
this.client = client;
}
CallLogAccessors.prototype = {
get callLogFilter() {
return this.client.helper.waitForElement(SELECTORS.callLogTabs);
},
get callLogNoResults() {
return this.client.helper.waitForElement(SELECTORS.callLogNoResults);
},
get logEntries() {
return this.client.findElements(SELECTORS.callLogItem);
},
get editButton() {
return this.client.helper.waitForElement(SELECTORS.editButton);
},
get editHeader() {
return new EditHeaderAccessors(this.client, SELECTORS.editHeader);
},
get selectAllButton() {
return this.client.helper.waitForElement(SELECTORS.selectAllButton);
},
get deleteButton() {
return this.client.helper.waitForElement(SELECTORS.deleteButton);
},
get dialogConfirmButton() {
return this.client.helper.waitForElement(SELECTORS.confirmDeleteButton);
}
};
var HEADER_SELECTORS = Object.freeze({
title: 'h1',
});
function EditHeaderAccessors(client, rootSelector) {
this.client = client;
this.rootSelector = rootSelector;
}
EditHeaderAccessors.prototype = {
get root() {
return this.client.helper.waitForElement(this.rootSelector);
},
get title() {
return this.root.findElement(HEADER_SELECTORS.title);
},
get closeButton() {
return this.root;
}
};
module.exports = CallLogAccessors;

View file

@ -0,0 +1,70 @@
'use strict';
var CallLogAccessors = require('./accessors');
function CallLogView(client, expectedNumberOfItems) {
this.client = client;
this.accessors = new CallLogAccessors(client);
this.accessors.callLogFilter;
if (expectedNumberOfItems === 0) {
this.accessors.callLogNoResults;
} else {
var self = this;
this.client.waitFor(function() {
return self.accessors.logEntries.length === expectedNumberOfItems;
});
}
}
CallLogView.prototype = {
enterEditMode: function() {
var filter = this.accessors.callLogFilter;
this.accessors.editButton.tap();
this.client.helper.waitForElementToDisappear(filter);
var self = this;
this.client.waitFor(function() {
return self.accessors.editHeader.title.getAttribute('data-l10n-id') ===
'edit';
});
},
exitEditMode: function() {
var editHeader = this.accessors.editHeader.root;
// TODO: Use a GaiaHeader view, once bug 1241813 lands
this.accessors.editHeader.closeButton.tap(10,10);
this.accessors.callLogFilter;
this.client.helper.waitForElementToDisappear(editHeader);
},
deleteEntries: function(indexesToDelete) {
var logEntries = this.accessors.logEntries;
var entriesToDelete = logEntries.filter(function(_, index) {
return indexesToDelete.indexOf(index) > -1;
});
entriesToDelete.forEach(function(entry) {
entry.tap();
});
this._performDelete();
},
deleteAllEntries: function() {
this.accessors.selectAllButton.tap();
this._performDelete();
},
_performDelete: function() {
var editHeader = this.accessors.editHeader.root;
this.accessors.deleteButton.tap();
this.accessors.dialogConfirmButton.tap();
this.client.helper.waitForElementToDisappear(editHeader);
}
};
module.exports = CallLogView;

View file

@ -0,0 +1,23 @@
'use strict';
/* global module */
var SELECTORS = Object.freeze({
goToCallLog: '#option-recents',
goToContactList: '#option-contacts'
});
function DialerOptionsAccessor(client) {
this.client = client;
}
DialerOptionsAccessor.prototype = {
get callLogButton() {
return this.client.helper.waitForElement(SELECTORS.goToCallLog);
},
get contactListButton() {
return this.client.helper.waitForElement(SELECTORS.goToContactList);
}
};
module.exports = DialerOptionsAccessor;

View file

@ -0,0 +1,28 @@
'use strict';
/* global module */
var DialerOptionsAccessor = require('./tabAccessors');
function DialerOptionsView(client) {
this.client = client;
this.accessors = new DialerOptionsAccessor(client);
}
DialerOptionsView.prototype = {
goToCallLog: function() {
var callLogTab = this.accessors.callLogButton;
callLogTab.tap();
var CallLogView = require('../call_log/views.js');
return new CallLogView(this.client);
},
goToContactList: function() {
var contactListTab = this.accessors.contactListButton;
contactListTab.tap();
var contacts = this.client.loader.getAppClass('contacts');
return contacts;
}
};
module.exports = DialerOptionsView;

View file

@ -0,0 +1,27 @@
'use strict';
/* global module */
var SELECTORS = Object.freeze({
keypadCallBarAddContact: '#keypad-callbar-add-contact',
});
function KeypadAccessor(client) {
this.client = client;
}
KeypadAccessor.prototype = {
get addContactButton() {
return this.client.helper.waitForElement(SELECTORS.keypadCallBarAddContact);
},
key: function(keyNumber) {
return this.client.helper.waitForElement(
this.getKeypadKeySelector(keyNumber)
);
},
getKeypadKeySelector: function(key) {
return '.keypad-key[data-value="' + key +'"]';
}
};
module.exports = KeypadAccessor;

View file

@ -0,0 +1,36 @@
'use strict';
/* global module */
var KeypadAccessor = require('./accessors');
function KeypadView(client) {
this.client = client;
this.accessors = new KeypadAccessor(client);
}
KeypadView.prototype = {
typePhoneNumber: function(number) {
var i;
var typeNumberArray = String(number).split('');
for (i in typeNumberArray){
if (i === '+') {
var zero_key = this.key(0);
this.actions.longPress(zero_key, 1).perform();
} else {
i = parseInt(typeNumberArray[i]);
this.accessors.key(i).tap();
}
}
},
addToContacts: function() {
var addToContactsButton = this.accessors.addContactButton;
addToContactsButton.tap();
var ActivitiesView = require('../activities/views.js');
var activitiesView = new ActivitiesView(this.client);
return activitiesView;
}
};
module.exports = KeypadView;

View file

@ -0,0 +1,22 @@
'use strict';
var SELECTORS = Object.freeze({
callLogTabItem: '#option-recents',
contactsTabItem: '#option-contacts'
});
function TabsAccessors(client) {
this.client = client;
}
TabsAccessors.prototype = {
get callLogButton() {
return this.client.helper.waitForElement(SELECTORS.callLogTabItem);
},
get contactListButton() {
return this.client.helper.waitForElement(SELECTORS.contactsTabItem);
}
};
module.exports = TabsAccessors;

View file

@ -0,0 +1,25 @@
'use strict';
var TabsAccessors = require('./accessors');
var CallLogView = require('../call_log/view.js');
function TabsView(client) {
this.client = client;
this.accessors = new TabsAccessors(client);
}
TabsView.prototype = {
goToCallLog: function(expectedNumberOfItems) {
expectedNumberOfItems = expectedNumberOfItems || 0;
this.accessors.callLogButton.tap();
return new CallLogView(this.client, expectedNumberOfItems);
},
goToContactList: function() {
this.accessors.contactListButton.tap();
var contacts = this.client.loader.getAppClass('contacts');
return contacts;
},
};
module.exports = TabsView;

View file

@ -0,0 +1,3 @@
[navigation_test.js]
[keypad_test.js]
[call_log_test.js]

View file

@ -0,0 +1,46 @@
'use strict';
var Dialer = require('./lib/dialer');
marionette('Dialer > Navigation', function() {
var assert = require('assert');
var client = marionette.client(Dialer.config);
var subject;
var selectors;
var actions;
setup(function() {
actions = client.loader.getActions();
subject = new Dialer(client);
subject.launch();
selectors = Dialer.Selectors;
var keypad = subject.client.findElement(selectors.keypadView);
client.waitFor(function() {
return keypad.displayed();
});
});
test('Call Log', function() {
var tabItem = subject.client.findElement(selectors.callLogTabItem);
actions.tap(tabItem).perform();
var callLogTabs = subject.client.findElement(selectors.callLogTabs);
client.waitFor(function() {
return callLogTabs.displayed();
});
assert.ok(true, 'displayed the call log tabs');
});
test('Contacts', function() {
var tabItem = subject.client.findElement(selectors.contactsTabItem);
actions.tap(tabItem).perform();
var iframe = subject.client.findElement('#iframe-contacts');
client.waitFor(function() {
return iframe.displayed();
});
assert.ok(true, 'displayed the contacts iframe');
});
});

View file

@ -0,0 +1,18 @@
{
"extends": "../../../../../.jshintrc",
"predef": [
"assert",
"suite",
"test",
"setup",
"teardown",
"suiteSetup",
"suiteTeardown",
"require",
"requireApp",
"sinon",
"mocha",
"loadBodyHTML",
"Promise"
]
}

View file

@ -0,0 +1,86 @@
'use strict';
/* globals MocksHelper, AddContactMenu, OptionMenu */
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_moz_activity.js');
require('/shared/test/unit/mocks/mock_option_menu.js');
require('/dialer/js/add_contact_menu.js');
var mocksHelperForPhoneActionMenu = new MocksHelper([
'LazyLoader',
'MozActivity',
'OptionMenu'
]).init();
suite('Add Contact menu', function() {
mocksHelperForPhoneActionMenu.attachTestHelpers();
var fakePhoneNumber = '123';
setup(function() {
AddContactMenu.show(fakePhoneNumber);
});
suite('Opening', function() {
test('should display the good options and header', function() {
assert.equal(OptionMenu.calls.length, 1);
var called = OptionMenu.calls[0];
assert.equal(called.items.length, 3);
assert.equal(called.header, fakePhoneNumber);
assert.equal(called.items[0].l10nId, 'createNewContact');
assert.equal(called.items[1].l10nId, 'addToExistingContact');
assert.equal(called.items[2].l10nId, 'cancel');
assert.isTrue(called.items[2].incomplete);
});
});
suite('Create new contact', function() {
setup(function() {
this.sinon.spy(window, 'MozActivity');
// Fake clicking on the first button
var item = OptionMenu.calls[0].items[0];
item.method.apply(null, item.params);
});
test('starts a new contact activity', function() {
sinon.assert.calledWith(window.MozActivity, {
name: 'new',
data: {
type: 'webcontacts/contact',
params: {
'tel': fakePhoneNumber
}
}}
);
});
});
suite('Add to existing contact', function() {
setup(function() {
this.sinon.spy(window, 'MozActivity');
// Fake clicking on the second button
var item = OptionMenu.calls[0].items[1];
item.method.apply(null, item.params);
});
test('starts a new contact activity', function() {
sinon.assert.calledWith(window.MozActivity, {
name: 'update',
data: {
type: 'webcontacts/contact',
params: {
'tel': fakePhoneNumber
}
}}
);
});
});
});

View file

@ -0,0 +1,82 @@
'use strict';
/* globals MocksHelper, CallInfo, CallGroupMenu, OptionMenu */
require('/dialer/test/unit/mock_call_info.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_moz_activity.js');
require('/shared/test/unit/mocks/mock_option_menu.js');
require('/dialer/js/call_group_menu.js');
var mocksHelperForPhoneActionMenu = new MocksHelper([
'CallInfo',
'LazyLoader',
'MozActivity',
'OptionMenu'
]).init();
suite('Call Group menu', function() {
mocksHelperForPhoneActionMenu.attachTestHelpers();
var fakePrimaryInfo = 'Jean-Baptiste Poquelin';
var fakePhoneNumber = '123';
var fakeDate = '123456789';
var fakeType = 'incoming';
setup(function() {
CallGroupMenu.show(fakePrimaryInfo, fakePhoneNumber, fakeDate, fakeType);
});
suite('Opening', function() {
test('should display the good options and header', function() {
assert.equal(OptionMenu.calls.length, 1);
var called = OptionMenu.calls[0];
assert.equal(called.items.length, 3);
assert.equal(called.header.textContent, fakePrimaryInfo);
assert.equal(called.items[0].l10nId, 'callInformation');
assert.equal(called.items[1].l10nId, 'sendSms');
assert.equal(called.items[2].l10nId, 'cancel');
assert.isTrue(called.items[2].incomplete);
});
});
suite('Displaying call information', function() {
setup(function() {
this.sinon.spy(CallInfo, 'show');
// Fake clicking on the first button
var item = OptionMenu.calls[0].items[0];
item.method.apply(null, item.params);
});
test('calls CallInfo', function() {
sinon.assert.calledWith(
CallInfo.show, fakePhoneNumber, fakeDate, fakeType);
});
});
suite('Sending a SMS', function() {
setup(function() {
this.sinon.spy(window, 'MozActivity');
// Fake clicking on the second button
var item = OptionMenu.calls[0].items[1];
item.method.apply(null, item.params);
});
test('starts a new SMS activity', function() {
sinon.assert.calledWith(window.MozActivity, {
name: 'new',
data: {
type: 'websms/sms',
number: '123'
}}
);
});
});
});

View file

@ -0,0 +1,612 @@
'use strict';
/* globals CallInfo, CallLog, CallLogDBManager, MockL10n, MocksHelper, Utils,
LazyLoader, MockContactsButtons, MockSimplePhoneMatcher,
MockSimplePhoneMatcherBestMatches, l10nAssert */
require('/dialer/test/unit/mock_call_log.js');
require('/dialer/test/unit/mock_call_log_db_manager.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_moz_activity.js');
require('/shared/test/unit/mocks/dialer/mock_utils.js');
require('/shared/test/unit/mocks/dialer/mock_contacts.js');
require('/shared/test/unit/mocks/contacts/mock_contacts_buttons.js');
require('/shared/test/unit/mocks/mock_simple_phone_matcher.js');
require('/dialer/js/call_info.js');
var mocksHelperForCallInfoView = new MocksHelper([
'CallLog',
'CallLogDBManager',
'LazyLoader',
'MozActivity',
'Utils',
'ContactsButtons',
'Contacts',
'SimplePhoneMatcher'
]).init();
suite('Call Info', function(argument) {
var realL10n;
mocksHelperForCallInfoView.attachTestHelpers();
var contactsIframe;
var phoneDetailsHTML;
var emailDetailsHTML;
var groupReturn;
var fakeNumber = '12345';
var fakeDate = '1';
var fakeType = 'incoming';
var fakeStatus = 'connected';
var injectFragmentAsSection = function(name) {
var section = document.createElement('section');
section.setAttribute('role', 'region');
section.id = name;
section.hidden = true;
var template = document.body.querySelector(
'element[name="' + name + '"] template');
section.appendChild(template.content.cloneNode(true));
document.body.appendChild(section);
return section.innerHTML;
};
var initCallInfo = function() {
document.body.innerHTML = '';
loadBodyHTML('/contacts/elements/phone_details.html');
var phoneDetailsTemplate = document.querySelector('element');
loadBodyHTML('/contacts/elements/email_details.html');
var emailDetailsTemplate = document.querySelector('element');
loadBodyHTML('/dialer/elements/call-info-view.html');
document.body.appendChild(phoneDetailsTemplate);
document.body.appendChild(emailDetailsTemplate);
phoneDetailsHTML = injectFragmentAsSection('phone-details');
emailDetailsHTML = injectFragmentAsSection('email-details');
injectFragmentAsSection('call-info-view');
contactsIframe = document.createElement('iframe');
contactsIframe.id = 'iframe-contacts';
document.body.appendChild(contactsIframe);
CallInfo._initialised = false;
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
};
var dispatchCloseViewEvent = function() {
var evt = new CustomEvent('action', {
detail: {
type: 'back'
}
});
document.getElementById('call-info-gaia-header').dispatchEvent(evt);
};
suiteSetup(function() {
realL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
});
suiteTeardown(function() {
navigator.mozL10n = realL10n;
});
setup(function() {
groupReturn = {
number: '12345',
date: 1,
type: 'incoming',
};
this.sinon.stub(CallLogDBManager, 'getGroup').returns({
then: function(callback) {
callback(groupReturn);
}
});
initCallInfo();
});
suite('fragment injection', function() {
var callInfoView;
setup(function() {
callInfoView = document.getElementById('call-info-view');
this.sinon.spy(LazyLoader, 'load');
initCallInfo();
});
test('lazy-loads styles', function() {
assert.include(LazyLoader.load.args[0][0],
'/shared/style/contacts/contacts_buttons.css');
assert.include(LazyLoader.load.args[0][0],
'/shared/style/contacts.css');
assert.include(LazyLoader.load.args[0][0],
'/dialer/style/buttons.css');
});
test('lazy-loads fragments', function() {
var phoneDetailsElt = document.getElementById('phone-details');
var emailDetailsElt = document.getElementById('email-details');
sinon.assert.calledWith(LazyLoader.load,
[phoneDetailsElt, emailDetailsElt]);
});
test('injects phone-details', function() {
var phoneDetailsElt = callInfoView.querySelector('#phone-details');
assert.equal(phoneDetailsElt.innerHTML, phoneDetailsHTML);
assert.isNull(document.getElementById('phone-details-stub'));
});
test('injects email-details', function() {
var emailDetailsElt = callInfoView.querySelector('#email-details');
assert.equal(emailDetailsElt.innerHTML, emailDetailsHTML);
assert.isNull(document.getElementById('email-details-stub'));
});
});
suite('ContactsButtons', function() {
test('initialized after loading fragments', function() {
this.sinon.spy(MockContactsButtons, 'init');
initCallInfo();
var listDetailsElt = document.getElementById('call-info-list-details');
var contactDetailsElt = document.getElementById('contact-detail');
sinon.assert.calledWith(MockContactsButtons.init,
listDetailsElt, contactDetailsElt);
});
test('without contact, renders phones', function() {
this.sinon.spy(MockContactsButtons, 'renderPhones');
initCallInfo();
var contact = {
tel: [
{
value: fakeNumber,
type: 'mobile'
}
]
};
sinon.assert.calledWith(MockContactsButtons.renderPhones, contact);
});
test('with contact, renders phones and emails', function() {
this.sinon.spy(MockContactsButtons, 'renderPhones');
this.sinon.spy(MockContactsButtons, 'renderEmails');
var contact = {
matchingTel: {
number: '54321'
}
};
var expectedContact = {
id: 'id',
name: ['test name'],
tel: [{
value: '54321',
carrier: 'carrier',
type: 'type'
}],
photo: ['test']
};
groupReturn.contact = contact;
initCallInfo();
sinon.assert.calledWith(MockContactsButtons.renderPhones,
expectedContact);
sinon.assert.calledWith(MockContactsButtons.renderEmails,
expectedContact);
});
['+112345', '012345', '12345,,,'].forEach(function(number) {
test('with contact, matches phones correctly with number ' + number,
function() {
fakeNumber = '12345';
this.sinon.stub(MockSimplePhoneMatcher, 'bestMatch').returns(
new MockSimplePhoneMatcherBestMatches());
var contact = {
matchingTel: {
number: fakeNumber
}
};
this.sinon.spy(MockContactsButtons, 'reMark');
groupReturn.contact = contact;
groupReturn.type = 'outgoing';
groupReturn.number = number;
initCallInfo();
sinon.assert.calledWith(MockContactsButtons.reMark,
'tel', fakeNumber, 'remark');
});
});
test('clears buttons before each render', function(done) {
initCallInfo();
var listDetailsElt = document.getElementById('call-info-list-details');
listDetailsElt.innerHTML = 'test<span>tset</span>';
function checkIsListDetailsClear() {
assert.equal(listDetailsElt.innerHTML, '');
done();
}
this.sinon.stub(MockContactsButtons, 'renderPhones',
checkIsListDetailsClear);
this.sinon.stub(MockContactsButtons, 'renderEmails',
checkIsListDetailsClear);
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
});
test('should highlight selected number as not missed', function() {
this.sinon.stub(MockSimplePhoneMatcher, 'bestMatch').returns(
new MockSimplePhoneMatcherBestMatches());
this.sinon.spy(MockContactsButtons, 'reMark');
groupReturn.type = 'outgoing';
initCallInfo();
sinon.assert.calledWith(MockContactsButtons.reMark,
'tel', fakeNumber, 'remark');
});
test('should highlight selected and incoming number as not missed',
function() {
this.sinon.stub(MockSimplePhoneMatcher, 'bestMatch').returns(
new MockSimplePhoneMatcherBestMatches());
this.sinon.spy(MockContactsButtons, 'reMark');
groupReturn.type = 'incoming';
groupReturn.status = 'connected';
initCallInfo();
sinon.assert.calledWith(MockContactsButtons.reMark,
'tel', fakeNumber, 'remark');
});
test('should highlight selected and missed number as missed', function() {
this.sinon.stub(MockSimplePhoneMatcher, 'bestMatch').returns(
new MockSimplePhoneMatcherBestMatches());
this.sinon.spy(MockContactsButtons, 'reMark');
groupReturn.type = 'incoming';
groupReturn.status = 'connecting';
initCallInfo();
sinon.assert.calledWith(MockContactsButtons.reMark,
'tel', fakeNumber, 'remark-missed');
});
});
suite('common', function() {
setup(function() {
this.sinon.stub(Utils, 'setHeaderDate', function(elem, time) {
elem.setAttribute('data-l10n-id', 'TADA');
});
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
});
test('displays the view', function() {
assert.isFalse(document.getElementById('call-info-view').hidden);
});
test('hide the call log edit button', function() {
assert.isTrue(CallLog.mCallLogEditModeButtonHidden);
});
test('looks up the right group', function() {
sinon.assert.calledWith(CallLogDBManager.getGroup,
fakeNumber, parseInt(fakeDate, 10), fakeType, fakeStatus);
});
test('tapping the close button closes the view', function() {
dispatchCloseViewEvent();
assert.isTrue(document.getElementById('call-info-view').hidden);
assert.isFalse(CallLog.mCallLogEditModeButtonHidden);
});
test('sets day for call list', function() {
l10nAssert(document.getElementById('call-info-day'), 'TADA');
});
test('view title is set', function() {
var callInfoTitle = document.getElementById('call-info-title');
assert.equal(callInfoTitle.textContent, fakeNumber);
});
suite('Number is voicemail', function() {
setup(function() {
groupReturn.voicemail = true;
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
});
teardown(function() {
groupReturn.voicemail = false;
});
test('view title is set', function() {
var callInfoTitle = document.getElementById('call-info-title');
assert.equal(callInfoTitle.getAttribute('data-l10n-id'), 'voiceMail');
});
});
suite('Number is emergency', function() {
setup(function() {
groupReturn.emergency = true;
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
});
test('view title is set', function() {
var callInfoTitle = document.getElementById('call-info-title');
assert.equal(callInfoTitle.getAttribute('data-l10n-id'),
'emergencyNumber');
});
});
suite('Call direction', function() {
var previousType;
var previousStatus;
var classList;
setup(function() {
previousType = groupReturn.type;
previousStatus = groupReturn.status;
classList = document.getElementById('call-info-direction').classList;
});
['dialing', 'alerting'].forEach(function(type) {
test('outgoing ' + type, function() {
groupReturn.type = type;
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
assert.isTrue(classList.contains('icon-outgoing'));
assert.isFalse(classList.contains('icon-incoming'));
assert.isFalse(classList.contains('icon-missed'));
});
});
test('incoming', function() {
groupReturn.type = 'incoming';
groupReturn.status = 'connected';
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
assert.isFalse(classList.contains('icon-outgoing'));
assert.isTrue(classList.contains('icon-incoming'));
assert.isFalse(classList.contains('icon-missed'));
});
test('missed', function() {
groupReturn.type = 'incoming';
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
assert.isFalse(classList.contains('icon-outgoing'));
assert.isFalse(classList.contains('icon-incoming'));
assert.isTrue(classList.contains('icon-missed'));
});
});
});
suite('displaying calls', function() {
setup(function() {
groupReturn.calls = [
{date: 123, duration: 0},
{date: 456, duration: 12},
{date: 789, duration: 9001}
];
this.sinon.stub(Utils, 'prettyDate').returns('wesh');
this.sinon.stub(Utils, 'prettyDuration').returnsArg(0);
this.sinon.useFakeTimers();
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
this.sinon.clock.tick();
});
test('creates three rows', function() {
var rows = document.getElementsByClassName('call-duration');
assert.equal(rows.length, 3);
});
test('displays a friendly start date', function() {
sinon.assert.calledWith(Utils.prettyDate, groupReturn.calls[0].date);
sinon.assert.calledWith(Utils.prettyDate, groupReturn.calls[1].date);
var startTimes = document.getElementsByClassName('cd__start-time');
for (var startTime of startTimes) {
assert.equal(startTime.textContent, 'wesh');
}
});
test('displays calls in the proper order', function() {
var durations = document.getElementsByClassName('cd__duration');
for (var i=1; i < durations.length; i++) {
sinon.assert.calledWith(Utils.prettyDuration, durations[i],
groupReturn.calls[i].duration, 'callDurationTextFormat');
}
});
suite('calls with 0 duration', function() {
var previousType;
setup(function() {
previousType = groupReturn.type;
});
test('canceled calls', function() {
groupReturn.type = 'dialing';
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
this.sinon.clock.tick();
var durations = document.getElementsByClassName('cd__duration');
assert.equal(durations[0].getAttribute('data-l10n-id'), 'canceled');
});
test('missed calls', function() {
groupReturn.type = 'incoming';
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
this.sinon.clock.tick();
var durations = document.getElementsByClassName('cd__duration');
assert.equal(durations[0].getAttribute('data-l10n-id'), 'info-missed');
});
});
test('adapts to timeformat changes', function() {
Utils.prettyDate.returns('wesh24!');
window.dispatchEvent(new CustomEvent('timeformatchange'));
var startTimeElts = document.querySelectorAll('.js-ci-start-times');
for (var i=0, il=startTimeElts.length; i<il; i++) {
sinon.assert.calledWith(Utils.prettyDate,
parseInt(startTimeElts[i].dataset.date, 10));
assert.equal(startTimeElts[i].textContent, 'wesh24!');
}
});
});
suite('action buttons', function() {
var detailsButton;
var addToContactButton;
var createContactButton;
setup(function() {
detailsButton = document.getElementById('call-info-details');
addToContactButton = document.getElementById('call-info-add');
createContactButton = document.getElementById('call-info-create');
this.sinon.spy(window, 'MozActivity');
});
suite('no contact', function() {
test('only contact creation ones are displayed', function() {
assert.isTrue(detailsButton.hidden);
assert.isFalse(addToContactButton.hidden);
assert.isFalse(createContactButton.hidden);
});
test('add a contact launches an activity', function() {
addToContactButton.click();
sinon.assert.calledWith(window.MozActivity, {
name: 'update',
data: {
type: 'webcontacts/contact',
params: {
'tel': fakeNumber
}
}
});
});
test('create a contact launches an activity', function() {
createContactButton.click();
sinon.assert.calledWith(window.MozActivity, {
name: 'new',
data: {
type: 'webcontacts/contact',
params: {
'tel': fakeNumber
}
}
});
});
});
suite('with a contact', function() {
setup(function() {
groupReturn.contact = {
primaryInfo: 'WESH',
matchingTel: {}
};
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
this.sinon.useFakeTimers();
});
test('only contact details is displayed', function() {
assert.isFalse(detailsButton.hidden);
assert.isTrue(addToContactButton.hidden);
assert.isTrue(createContactButton.hidden);
});
test('viewing contact details work', function() {
detailsButton.click();
sinon.assert.calledWith(window.MozActivity, {
name: 'open',
data: {
type: 'webcontacts/contact',
params: {
'id': groupReturn.contact.id
}
}
});
});
});
});
suite('Displaying a contact', function() {
setup(function() {
groupReturn.contact = {
primaryInfo: 'WESH',
matchingTel: {}
};
CallInfo.show(fakeNumber, fakeDate, fakeType, fakeStatus);
});
test('view title is set', function() {
var callInfoTitle = document.getElementById('call-info-title');
assert.equal(callInfoTitle.textContent, groupReturn.contact.primaryInfo);
});
});
suite('Updates after call log db updates', function() {
test('adds a new call', function() {
groupReturn.calls = [
{date: 123, duration: 0}
];
var createOrUpdateEvt = new CustomEvent('CallLogDbNewCall',
{detail: {group: groupReturn}});
var rows = document.getElementsByClassName('call-duration');
assert.equal(rows.length, 0);
window.dispatchEvent(createOrUpdateEvt);
assert.equal(rows.length, 1);
});
test('does not listen when the view is closed', function() {
dispatchCloseViewEvent();
groupReturn.calls = [
{date: 123, duration: 0},
{date: 123, duration: 0}
];
var createOrUpdateEvt = new CustomEvent('CallLogDbNewCall',
{detail: {group: groupReturn}});
var rows = document.getElementsByClassName('call-duration');
assert.equal(rows.length, 0);
window.dispatchEvent(createOrUpdateEvt);
assert.equal(rows.length, 0);
});
});
});

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,789 @@
'use strict';
/* global CallHandler, MocksHelper, MockL10n, MockNavigatormozApps,
MockNavigatorMozIccManager, MockNavigatormozSetMessageHandler,
NavbarManager, NotificationHelper, MockKeypadManager, MockVoicemail,
MockCallLog, MockCallLogDBManager, MockNavigatorWakeLock, MockMmiManager,
MockSimSettingsHelper, MockTelephonyHelper, MockSettingsListener,
CustomElementsHelper, MockNavigation */
require(
'/shared/test/unit/mocks/mock_navigator_moz_set_message_handler.js'
);
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_navigator_wake_lock.js');
require('/shared/test/unit/mocks/mock_voicemail.js');
require('/dialer/test/unit/mock_call_log.js');
require('/dialer/test/unit/mock_call_log_db_manager.js');
require('/dialer/test/unit/mock_mmi_manager.js');
require('/dialer/test/unit/mock_suggestion_bar.js');
require('/shared/test/unit/mocks/mock_accessibility_helper.js');
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
require('/shared/test/unit/mocks/mock_navigator_moz_icc_manager.js');
require('/shared/test/unit/mocks/mock_notification.js');
require('/shared/test/unit/mocks/mock_notification_helper.js');
require('/shared/test/unit/mocks/mock_settings_listener.js');
require('/shared/test/unit/mocks/mock_sim_settings_helper.js');
require('/shared/test/unit/mocks/dialer/mock_contacts.js');
require('/shared/test/unit/mocks/dialer/mock_keypad.js');
require('/shared/test/unit/mocks/dialer/mock_telephony_helper.js');
require('/shared/test/unit/mocks/dialer/mock_tone_player.js');
require('/shared/test/unit/mocks/dialer/mock_utils.js');
require('/shared/test/unit/mocks/mock_moz_activity.js');
require(
'/shared/test/unit/mocks/elements/gaia_sim_picker/mock_gaia_sim_picker.js');
require('/dialer/test/unit/mock_navigation.js');
require('/dialer/js/dialer.js');
var mocksHelperForDialer = new MocksHelper([
'Navigation',
'TelephonyHelper',
'AccessibilityHelper',
'Contacts',
'CallLog',
'CallLogDBManager',
'GaiaSimPicker',
'LazyLoader',
'KeypadManager',
'MmiManager',
'MozActivity',
'Notification',
'NotificationHelper',
'SettingsListener',
'SimSettingsHelper',
'SuggestionBar',
'Utils',
'TonePlayer',
'Voicemail'
]).init();
var customElementsForNavbarManager = new CustomElementsHelper([
'GaiaSimPicker'
]);
suite('navigation bar', function() {
var domViews;
var realMozApps;
var realMozIccManager;
var realMozL10n;
var realSetMessageHandler;
var realWakeLock;
mocksHelperForDialer.attachTestHelpers();
suiteSetup(function() {
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
});
suiteTeardown(function() {
navigator.mozL10n = realMozL10n;
});
setup(function() {
realMozApps = navigator.mozApps;
navigator.mozApps = MockNavigatormozApps;
realMozIccManager = navigator.mozIccManager;
navigator.mozIccManager = MockNavigatorMozIccManager;
realSetMessageHandler = navigator.mozSetMessageHandler;
navigator.mozSetMessageHandler = MockNavigatormozSetMessageHandler;
MockNavigatormozSetMessageHandler.mSetup();
realWakeLock = navigator.requestWakeLock;
navigator.requestWakeLock = MockNavigatorWakeLock.requestWakeLock;
domViews = document.createElement('section');
domViews.id = 'views';
document.body.appendChild(domViews);
CallHandler.init();
NavbarManager.init();
customElementsForNavbarManager.resolve();
});
teardown(function() {
MockNavigatorMozIccManager.mTeardown();
navigator.mozIccManager = realMozIccManager;
MockNavigatormozSetMessageHandler.mTeardown();
navigator.mozSetMessageHandler = realSetMessageHandler;
MockNavigatormozApps.mTeardown();
navigator.mozApps = realMozApps;
MockNavigatorWakeLock.mTeardown();
navigator.requestWakeLock = realWakeLock;
document.body.removeChild(domViews);
});
suite('Initialization', function() {
test('the keypad should be shown', function() {
this.sinon.spy(MockNavigation, 'show');
NavbarManager.init();
sinon.assert.calledWith(MockNavigation.show, 'keypad');
});
});
suite('CallHandler', function() {
suite('> missed call notification', function() {
var callEndedData;
setup(function() {
this.sinon.spy(NotificationHelper, 'send');
MockNavigatorMozIccManager.addIcc('12345', {'cardState': 'ready'});
callEndedData = {
number: '123',
serviceId: 1,
direction: 'incoming',
hangUpLocal: false
};
});
suite('> One SIM', function() {
setup(function() {
MockNavigatormozSetMessageHandler.mTrigger('telephony-call-ended',
callEndedData);
MockNavigatormozApps.mTriggerLastRequestSuccess();
});
test('should send the notification with a localized message',
function() {
sinon.assert.calledWith(NotificationHelper.send, 'missedCall');
});
});
suite('> Two SIMs', function() {
setup(function() {
MockNavigatorMozIccManager.addIcc('6789', {
'cardState': 'ready'
});
MockNavigatormozSetMessageHandler.mTrigger('telephony-call-ended',
callEndedData);
MockNavigatormozApps.mTriggerLastRequestSuccess();
});
test('should send the notification with a localized message',
function() {
sinon.assert.calledWithMatch(NotificationHelper.send, {
id: 'missedCallMultiSims',
args: { n: 2 }
});
});
});
suite('> Rejected call', function() {
setup(function() {
callEndedData.hangUpLocal = true;
MockNavigatormozSetMessageHandler.mTrigger('telephony-call-ended',
callEndedData);
});
test('does not send notification', function() {
// Requesting an app indicates that we are sending a notification.
assert.isNull(MockNavigatormozApps.mLastRequest);
});
});
});
suite('> insertion in the call log database', function() {
var sysMsg;
var addStub;
function triggerSysMsg(data) {
MockNavigatormozSetMessageHandler.mTrigger('telephony-call-ended',
data);
}
setup(function() {
sysMsg = {
number: '12345',
serviceId: 1,
emergency: false,
duration: 1200,
direction: 'outgoing'
};
});
setup(function() {
addStub = this.sinon.stub(MockCallLogDBManager, 'add');
});
test('should require a high priority wake lock', function() {
triggerSysMsg(sysMsg);
var wakeLock = MockNavigatorWakeLock.mLastWakeLock;
assert.equal(wakeLock.topic, 'high-priority');
});
suite('> voicemail', function() {
setup(function() {
this.sinon.spy(MockVoicemail, 'check');
triggerSysMsg(sysMsg);
});
test('should check if the number if a voicemail', function() {
sinon.assert.calledWith(MockVoicemail.check, '12345',
sysMsg.serviceId);
});
test('should flag the entry as voicemail if it is', function() {
MockVoicemail.mResolvePromise(true);
sinon.assert.calledWithMatch(addStub, {voicemail: true});
});
test('should not flag the entry if it is not', function() {
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {voicemail: false});
});
});
suite('> date', function() {
test('should be set to now minus the call duration', function() {
this.sinon.useFakeTimers(4200);
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {date: 3000});
});
});
test('should set the duration', function() {
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {duration: 1200});
});
suite('> type', function() {
test('should be incoming for incoming calls', function() {
sysMsg.direction = 'incoming';
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {type: 'incoming'});
});
test('should be alerting for outgoing calls', function() {
sysMsg.direction = 'outgoing';
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {type: 'dialing'});
});
});
test('should set the phone number', function() {
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {number: '12345'});
});
suite('> with a CDMA second call', function() {
setup(function() {
sysMsg.secondNumber = '23456';
});
test('should insert two different calls in the database', function() {
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {number: '12345'});
addStub.yield();
sinon.assert.calledWithMatch(addStub, {number: '23456'});
});
test('should insert the secondCall in the database', function() {
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
addStub.yield();
sinon.assert.calledWithMatch(addStub, {
duration: 1200,
type: 'incoming',
number: '23456',
serviceId: 1,
emergency: false,
voicemail: false,
status: 'connected'
});
});
test('should also insert new call waiting group for cdma log view',
function() {
var fakeCdmaGroup = 'random useless string';
var fakeCdmaGroupSecondCall = 'another random string';
var appendSpy = this.sinon.spy(MockCallLog, 'appendGroup');
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
addStub.yield(fakeCdmaGroup);
addStub.yield(fakeCdmaGroupSecondCall);
sinon.assert.calledWith(appendSpy, fakeCdmaGroup);
sinon.assert.calledWith(appendSpy, fakeCdmaGroupSecondCall);
});
test('should only unlock after second call is added',
function() {
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
var wakeLock = MockNavigatorWakeLock.mLastWakeLock;
addStub.yield();
assert.equal(wakeLock.mUnlockCount, 0);
addStub.yield();
assert.equal(wakeLock.mUnlockCount, 1);
});
});
test('should set the serviceId', function() {
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {serviceId: 1});
});
suite('> emergency', function() {
test('should flag the entry as emergency if it is', function() {
sysMsg.emergency = true;
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {emergency: true});
});
test('should not flag the entry if it is not', function() {
sysMsg.emergency = null;
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {emergency: false});
});
});
suite('> status', function() {
test('should be connected for incoming connected calls', function() {
sysMsg.direction = 'incoming';
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {status: 'connected'});
});
test('should be null otherwise', function() {
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
sinon.assert.calledWithMatch(addStub, {status: null});
});
});
test('should insert the newly inserted group in the call log view',
function() {
var fakeGroup = '----uniq----';
var appendSpy = this.sinon.spy(MockCallLog, 'appendGroup');
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
addStub.yield(fakeGroup);
sinon.assert.calledWith(appendSpy, fakeGroup);
});
test('should release the wake lock', function() {
triggerSysMsg(sysMsg);
MockVoicemail.mResolvePromise(false);
addStub.yield();
var wakeLock = MockNavigatorWakeLock.mLastWakeLock;
assert.isTrue(wakeLock.released);
});
});
suite('> Receiving a ussd', function() {
function triggerSysMsg(serviceId, session, message) {
MockNavigatormozSetMessageHandler.mTrigger('ussd-received', {
message: (message !== undefined) ? message : 'testing',
session: session || null,
serviceId: serviceId || 0
});
}
var realHidden, stubHidden;
setup(function() {
realHidden = document.hidden;
Object.defineProperty(document, 'hidden', {
configurable: true,
get: function() { return stubHidden; }
});
this.sinon.useFakeTimers();
});
teardown(function() {
Object.defineProperty(document, 'hidden', {
configurable: true,
get: function() { return realHidden; }
});
});
test('should call the MmiManager', function() {
this.sinon.spy(MockMmiManager, 'handleMMIReceived');
triggerSysMsg();
sinon.assert.calledWith(MockMmiManager.handleMMIReceived,
'testing', null, 0);
});
suite('when the app is visible', function() {
setup(function() {
stubHidden = false;
});
test('should not require a high priority wake lock', function() {
triggerSysMsg();
var wakeLock = MockNavigatorWakeLock.mLastWakeLock;
assert.isUndefined(wakeLock);
});
});
suite('when the app is invisible', function() {
setup(function() {
stubHidden = true;
});
test('should require a high priority wake lock', function() {
triggerSysMsg();
var wakeLock = MockNavigatorWakeLock.mLastWakeLock;
assert.equal(wakeLock.topic, 'high-priority');
});
test('should send a notification for unsolicited messages', function() {
this.sinon.spy(MockMmiManager, 'sendNotification');
triggerSysMsg(0, null);
sinon.assert.calledOnce(MockMmiManager.sendNotification);
var wakeLock = MockNavigatorWakeLock.mLastWakeLock;
assert.isTrue(wakeLock.released);
});
test('should not send a notification for empty messages',
function() {
this.sinon.spy(MockMmiManager, 'sendNotification');
triggerSysMsg(0, null, null);
sinon.assert.notCalled(MockMmiManager.sendNotification);
var wakeLock = MockNavigatorWakeLock.mLastWakeLock;
assert.isTrue(wakeLock.released);
});
suite('once the app is visible', function() {
setup(function() {
triggerSysMsg();
stubHidden = false;
});
test('should release the wake lock', function() {
document.dispatchEvent(new CustomEvent('visibilitychange'));
var wakeLock = MockNavigatorWakeLock.mLastWakeLock;
assert.isTrue(wakeLock.released);
});
});
suite('after a safety timeout', function() {
setup(function() {
triggerSysMsg();
});
test('should release the wake lock', function() {
this.sinon.clock.tick(30000);
var wakeLock = MockNavigatorWakeLock.mLastWakeLock;
assert.isTrue(wakeLock.released);
});
});
});
});
suite('> Launch engineering mode app by Activity', function() {
var code = '1234567';
setup(function() {
MockSettingsListener.mCallbacks['engineering-mode.key'](code);
});
teardown(function() {
MockSettingsListener.mCallbacks['engineering-mode.key'](null);
});
test('> Dialing engineering mode code', function() {
var activitySpy = this.sinon.spy(window, 'MozActivity');
CallHandler.call(code, 0);
sinon.assert.calledWithNew(activitySpy);
sinon.assert.calledOnce(activitySpy);
assert.deepEqual(activitySpy.firstCall.args, [{
name: 'internal-system-engineering-mode',
}]);
});
test('> Dialing a normal code should not trigger engineering mode',
function() {
var activitySpy = this.sinon.spy(window, 'MozActivity');
CallHandler.call('7654321', 0);
sinon.assert.notCalled(activitySpy);
});
});
suite('> Dialing MMI codes', function() {
setup(function (){
this.sinon.spy(MockTelephonyHelper, 'call');
});
[0, 1].forEach(function(cardIndex) {
test('> Dialing a generic code on SIM ' + cardIndex, function() {
var number = '*123#';
CallHandler.call(number, cardIndex);
sinon.assert.calledWith(MockTelephonyHelper.call, number, cardIndex);
});
test('> Requesting the IMEI codes on SIM ' + cardIndex, function() {
this.sinon.stub(MockMmiManager, 'isImei').returns(true);
this.sinon.spy(MockMmiManager, 'showImei');
CallHandler.call('*#06#', cardIndex);
sinon.assert.calledOnce(MockMmiManager.showImei);
sinon.assert.notCalled(MockTelephonyHelper.call);
});
});
});
suite('> dialing a long number', function() {
var spy, number;
setup(function() {
number = '+8801535479509';
spy = this.sinon.spy(MockKeypadManager, 'updatePhoneNumber');
});
test('display the number back properly if the call errors',
function(done) {
MockTelephonyHelper.mCallPromise = Promise.reject();
CallHandler.call(number, 0);
MockTelephonyHelper.mCallPromise.then(function() {
sinon.assert.calledWithMatch(spy, number, 'begin', false);
}).then(done, done);
});
});
suite('> bluetooth commands', function() {
function sendCommand(command) {
MockNavigatormozSetMessageHandler.mTrigger('bluetooth-dialer-command', {
command: command
});
}
var getGroupAtPositionStub;
var callSpy;
var simPicker;
setup(function() {
getGroupAtPositionStub =
this.sinon.stub(MockCallLogDBManager, 'getGroupAtPosition');
callSpy = this.sinon.stub(CallHandler, 'call');
simPicker = document.createElement('gaia-sim-picker');
simPicker.id = 'sim-picker';
document.body.appendChild(simPicker);
customElementsForNavbarManager.resolve();
});
teardown(function() {
document.body.removeChild(simPicker);
});
[0, 1].forEach(function(serviceId) {
test('> Dialing a specific number on user preferred SIM ' + serviceId,
function() {
MockSimSettingsHelper._defaultCards.outgoingCall = serviceId;
sendCommand('ATD12345');
sinon.assert.calledWith(callSpy, '12345', serviceId);
});
});
suite('> Dialing a specific number with user preferred SIM always ask',
function() {
var serviceId;
setup(function() {
serviceId = MockSimSettingsHelper._defaultCards.outgoingCall =
MockSimSettingsHelper.ALWAYS_ASK_OPTION_VALUE;
});
test('should show SIM picker', function() {
this.sinon.spy(simPicker, 'getOrPick');
sendCommand('ATD12345');
sinon.assert.calledWith(simPicker.getOrPick, serviceId, '12345');
});
test('should show/foreground the dialer', function() {
sendCommand('ATD12345');
MockNavigatormozApps.mTriggerLastRequestSuccess();
assert.isTrue(MockNavigatormozApps.mAppWasLaunched);
assert.equal(MockNavigatormozApps.mAppWasLaunchedWithEntryPoint,
'dialer');
});
});
suite('> Dialing the last recent entry', function() {
setup(function() {
sendCommand('BLDN');
});
test('should call getGroupAtPosition with correct position',
function() {
sinon.assert.calledWith(
getGroupAtPositionStub, 1, 'lastEntryDate', true);
});
[0, 1].forEach(function(serviceId) {
test('should dial on user preferred SIM ' + serviceId,
function() {
MockSimSettingsHelper._defaultCards.outgoingCall = serviceId;
getGroupAtPositionStub.yield({number: '424242'});
sinon.assert.calledWith(callSpy, '424242', serviceId);
});
});
[0, 1].forEach(function(serviceId) {
test('should use serviceId (' + serviceId + ') of last call',
function() {
MockSimSettingsHelper._defaultCards.outgoingCall =
MockSimSettingsHelper.ALWAYS_ASK_OPTION_VALUE;
getGroupAtPositionStub.yield(
{number: '424242', serviceId: serviceId});
sinon.assert.calledWith(callSpy, '424242', serviceId);
});
});
});
suite('> Dialing a specific recent entry', function() {
setup(function() {
sendCommand('ATD>3');
});
test('should call getGroupAtPosition with correct position',
function() {
sinon.assert.calledWith(
getGroupAtPositionStub, 3, 'lastEntryDate', true, 'dialing');
});
[0, 1].forEach(function(serviceId) {
test('should dial on user preferred SIM ' + serviceId,
function() {
MockSimSettingsHelper._defaultCards.outgoingCall = serviceId;
getGroupAtPositionStub.yield({number: '333'});
sinon.assert.calledWith(callSpy, '333', serviceId);
});
});
[0, 1].forEach(function(serviceId) {
test('should use serviceId (' + serviceId + ') of 3rd last call',
function() {
MockSimSettingsHelper._defaultCards.outgoingCall =
MockSimSettingsHelper.ALWAYS_ASK_OPTION_VALUE;
getGroupAtPositionStub.yield({number: '333', serviceId: serviceId});
sinon.assert.calledWith(callSpy, '333', serviceId);
});
});
});
});
suite('> WebActivities support', function() {
var activity;
var originalHash;
function triggerActivity(activity) {
MockNavigatormozSetMessageHandler.mTrigger('activity', activity);
}
setup(function() {
MockNavigation.showCalllog();
originalHash = window.location.hash;
activity = {
source: {
name: 'dial',
data: {
type: 'webtelephony/number',
number: '12345'
}
}
};
});
suite('> dial activity with a number', function() {
test('should fill the phone number view', function() {
var spy = this.sinon.spy(MockKeypadManager, 'updatePhoneNumber');
triggerActivity(activity);
sinon.assert.calledWith(spy, '12345', 'begin', false);
});
test('should show the keypad view', function() {
triggerActivity(activity);
assert.equal(MockNavigation.currentView, 'keypad');
});
});
suite('> dial without a number', function() {
setup(function() {
activity.source.data.number = '';
triggerActivity(activity);
});
test('should show the contacts view', function() {
assert.equal(MockNavigation.currentView, 'contacts');
});
});
});
});
suite('NavbarManager', function() {
suite('> show / hide', function() {
test('NavbarManager.hide() should hide navbar', function() {
NavbarManager.hide();
assert.isTrue(domViews.classList.contains('hide-toolbar'));
});
test('NavbarManager.show() should show navbar', function() {
NavbarManager.show();
assert.isFalse(domViews.classList.contains('hide-toolbar'));
});
});
});
suite('window resize', function() {
var stubInnerHeight;
setup(function() {
Object.defineProperty(window, 'innerHeight', {
configurable: true,
get: function() { return stubInnerHeight; }
});
});
teardown(function() {
delete window.innerHeight;
});
test('should hide the navbar when the keyboard is displayed', function() {
stubInnerHeight = 439;
this.sinon.stub(NavbarManager, 'hide');
window.onresize();
sinon.assert.called(NavbarManager.hide);
});
test('should display the navbar when the keyboard is absent', function() {
stubInnerHeight = 441;
this.sinon.stub(NavbarManager, 'show');
window.onresize();
sinon.assert.called(NavbarManager.show);
});
});
});

View file

@ -0,0 +1,60 @@
/* globals onLoadDialer, MockKeypadManager, MockL10n, MocksHelper */
'use strict';
require('/shared/test/unit/mocks/dialer/mock_keypad.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/dialer/test/unit/mock_call_handler.js');
require('/dialer/test/unit/mock_navbar_manager.js');
var mocksHelperForIndex = new MocksHelper([
'CallHandler',
'KeypadManager',
'LazyLoader',
'NavbarManager'
]).init();
suite('index.js', function() {
var realMozL10n;
mocksHelperForIndex.attachTestHelpers();
suiteSetup(function() {
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
});
suiteTeardown(function() {
navigator.mozL10n = realMozL10n;
});
setup(function(done) {
require('/dialer/js/index.js', done);
});
teardown(function() {
window.removeEventListener('load', onLoadDialer);
});
suite('initialization', function() {
var realMozAudioChannelManager;
setup(function() {
this.sinon.spy(MockKeypadManager, 'init');
realMozAudioChannelManager = navigator.mozAudioChannelManager;
navigator.mozAudioChannelManager = { volumeControlChannel: null };
onLoadDialer();
});
teardown(function() {
navigator.mozAudioChannelManager = realMozAudioChannelManager;
});
test('the KeypadManager is setup for use outside of a call',
function() {
sinon.assert.calledWith(MockKeypadManager.init, /* oncall */ false);
});
});
});

View file

@ -0,0 +1,892 @@
/* globals CallHandler, CallLogDBManager, CallScreen, ConfirmDialog,
CustomDialog, CustomElementsHelper, FontSizeManager,
gTonesFrequencies, KeypadManager, MockCall, MockCallsHandler,
MockIccManager, MockL10n, MockMozActivity,
MockMultiSimActionButtonSingleton, MockNavigatorMozTelephony,
MockNavigatorSettings, MockSettingsListener, MocksHelper,
MockTonePlayer, SimSettingsHelper, telephonyAddCall
*/
'use strict';
require('/shared/js/dialer/dtmf_tone.js');
require('/shared/js/dialer/keypad.js');
require('/contacts/test/unit/mock_confirm_dialog.js');
require('/dialer/test/unit/mock_call_handler.js');
require('/dialer/test/unit/mock_call_log_db_manager.js');
require('/shared/test/unit/mocks/mock_iccmanager.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_multi_sim_action_button.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_navigator_moz_telephony.js');
require('/shared/test/unit/mocks/mock_settings_listener.js');
require('/shared/test/unit/mocks/mock_sim_settings_helper.js');
require('/shared/test/unit/mocks/dialer/mock_handled_call.js');
require('/shared/test/unit/mocks/dialer/mock_call.js');
require('/shared/test/unit/mocks/dialer/mock_calls_handler.js');
require('/shared/test/unit/mocks/dialer/mock_utils.js');
require('/shared/test/unit/mocks/dialer/mock_tone_player.js');
require('/shared/test/unit/mocks/mock_custom_dialog.js');
require('/shared/test/unit/mocks/mock_moz_activity.js');
require('/shared/test/unit/mocks/dialer/mock_font_size_manager.js');
require('/dialer/test/unit/mock_dialer_index.html.js');
require(
'/shared/test/unit/mocks/elements/gaia_sim_picker/mock_gaia_sim_picker.js');
var mocksHelperForKeypad = new MocksHelper([
'LazyLoader',
'Utils',
'MultiSimActionButton',
'CallsHandler',
'CallHandler',
'CallLogDBManager',
'ConfirmDialog',
'HandledCall',
'SettingsListener',
'SimSettingsHelper',
'GaiaSimPicker',
'TonePlayer',
'CustomDialog',
'MozActivity',
'FontSizeManager'
]).init();
var customElementsHelperForKeypad = new CustomElementsHelper([
'GaiaSimPicker'
]);
suite('dialer/keypad', function() {
var subject;
var realMozActivity;
var realMozIccManager;
var realMozL10n;
var realMozSettings;
var realMozTelephony;
mocksHelperForKeypad.attachTestHelpers();
// Dummy node used as event target
var dummyNode = document.createElement('div');
/**
* Simulate a touchstart event
*
* @param key {String} The target's dataset value, the touched key
*/
function mockTouchStart(key) {
var fakeEvent = {
target: dummyNode,
preventDefault: function() {},
stopPropagation: function() {},
type: 'touchstart'
};
dummyNode.dataset.value = key;
subject.keyHandler(fakeEvent);
}
/**
* Simulate a touchmove event
*
* @param x {Integer} Horizontal coordinate of the touch event
* @param y {Integer} Vertical coordinate of the touch event
*/
function mockTouchMove(x, y) {
var fakeEvent = {
target: dummyNode,
preventDefault: function() {},
stopPropagation: function() {},
type: 'touchmove',
touches: [ { pageX: x, pageY: y } ]
};
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);
}
/**
* Simulate a long-press event
*
* @param key {String} The target's dataset value, the touched key
* @param clock {Object} The fake sinon clock used in this test
* @param duration {Integer} The duration of the event in ms (default: 400)
*/
function mockLongPress(key, clock, duration) {
mockTouchStart(key);
clock.tick(duration || 400);
mockTouchEnd(key);
}
/**
* Send the fake events needed to emulate the typing of an umber.
*
* @param {String} number The number to type.
*/
function mockTypeNumber(number) {
for (var i = 0, end = number.length; i < end; i++) {
mockTouchStart(number[i]);
mockTouchEnd(number[i]);
}
}
suiteSetup(function() {
realMozIccManager = navigator.mozIccManager;
navigator.mozIccManager = new MockIccManager();
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
realMozSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
MockNavigatorSettings.mSyncRepliesOnly = true;
realMozTelephony = navigator.mozTelephony;
navigator.mozTelephony = MockNavigatorMozTelephony;
loadBodyHTML('/dialer/test/unit/mock_dialer_index.html');
subject = KeypadManager;
subject.init(/* oncall */ false);
customElementsHelperForKeypad.resolve();
});
suiteTeardown(function() {
navigator.mozIccManager = realMozIccManager;
navigator.mozL10n = realMozL10n;
navigator.mozSettings = realMozSettings;
MockNavigatorSettings.mSyncRepliesOnly = false;
MockNavigatorMozTelephony.mSuiteTeardown();
navigator.mozTelephony = realMozTelephony;
});
setup(function() {
this.sinon.useFakeTimers();
dummyNode = document.createElement('div');
subject._phoneNumber = '';
});
teardown(function() {
dummyNode = null;
MockNavigatorMozTelephony.mTeardown();
});
suite('Keypad Manager', function() {
test('initializates the TonePlayer to use the system audio channel',
function() {
this.sinon.spy(MockTonePlayer, 'init');
KeypadManager.init(/* oncall */ false);
sinon.assert.calledOnce(MockTonePlayer.init);
sinon.assert.calledWithExactly(MockTonePlayer.init, 'system');
});
test('sanitizePhoneNumber', function(done) {
var testCases = {
'111-111-1111': '111-111-1111',
' 222-222-2222 ': '222-222-2222',
' 333 - 333 - 3333 ': '333-333-3333',
'4444 4444 4444': '444444444444',
'555\n555\n5555': '5555555555',
'666\t666\t6666': '6666666666'
};
var counter = 0;
function verifyNumber(index) {
return function() {
var sanitized = subject.sanitizePhoneNumber(index);
assert.equal(sanitized, testCases[index]);
};
}
for (var i in testCases) {
counter++;
test('#sanitizePhoneNumber - test case ' + counter, verifyNumber(i));
}
done();
});
suite('Abbreviated dialing codes', function() {
var mmi = '*#06#';
var speedDialNum = '1#';
test('Properly highlight the last key in an abbreviated dialing code',
function() {
for (var i = 0, end = mmi.length; i < end; i++) {
mockTouchStart(mmi[i]);
assert.isTrue(dummyNode.classList.contains('active'));
mockTouchEnd(mmi[i]);
assert.isFalse(dummyNode.classList.contains('active'));
}
});
test('Start an abbreviated dialing code operation only upon pressing #',
function() {
this.sinon.spy(KeypadManager, '_getSpeedDialNumber');
/* This simulates the user having typed a number with a # in the middle
* without triggering a an abbreviated dial operation, possibly by
* moving the cursor to insert the #. In this scenario an abbreviated
* dialing operation should not be triggered. */
subject._phoneNumber = '1#1';
mockTouchStart('delete');
mockTouchEnd('delete');
sinon.assert.notCalled(KeypadManager._getSpeedDialNumber);
});
test('Get IMEI via send MMI', function() {
this.sinon.spy(MockMultiSimActionButtonSingleton, 'performAction');
mockTypeNumber(mmi);
sinon.assert.calledOnce(
MockMultiSimActionButtonSingleton.performAction);
});
test('Starts speed dial upon typing ' + speedDialNum, function() {
this.sinon.stub(KeypadManager, '_getSpeedDialNumber', function() {
return Promise.resolve('123');
});
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.fetchLastCalled();
assert.equal(subject._phoneNumber, '');
});
test('Call button pressed with an incoming call and no outgoing calls ' +
'in Call Log', function() {
var recentCall = {
number: '666666666',
type: 'incoming',
date: Date.now(),
status: 'connected'
};
CallLogDBManager.add(recentCall, function(result) {
subject.fetchLastCalled();
assert.equal(subject._phoneNumber, '');
});
});
test('Call button pressed with outgoing call in Call Log', function() {
var recentCall = {
number: '666666666',
type: 'dialing',
date: Date.now()
};
CallLogDBManager.add(recentCall);
subject.fetchLastCalled();
assert.equal(subject._phoneNumber, recentCall.number);
});
test('Dialer is limited to 50 digits', function() {
var digits = '111111111122222222223333333333444444444455555555556';
mockTypeNumber(digits);
assert.equal(subject._phoneNumber, digits.substring(0, 50));
});
test('Adds active class to keys when pressed', function() {
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() {
this.sinon.spy(FontSizeManager, 'adaptToSpace');
subject.updatePhoneNumber('1234567890', 'begin', false);
sinon.assert.calledWith(
FontSizeManager.adaptToSpace, FontSizeManager.DIAL_PAD,
subject.phoneNumberView, false, 'begin');
});
suite('Audible and DTMF tones when composing numbers', function() {
setup(function() {
subject._observePreferences();
MockSettingsListener.mCallbacks['phone.ring.keypad'](true);
});
test('Pressing a button plays a short tone', function() {
var startSpy = this.sinon.spy(MockTonePlayer, 'start');
var stopSpy = this.sinon.spy(MockTonePlayer, 'stop');
mockTouchStart('1');
assert.isTrue(startSpy.calledWith(gTonesFrequencies['1'], true));
mockTouchEnd('1');
assert.isTrue(stopSpy.calledOnce);
});
test('Button tones are disabled via prefs', function() {
var startSpy = this.sinon.spy(MockTonePlayer, 'start');
MockSettingsListener.mCallbacks['phone.ring.keypad'](false);
mockTouchStart('1');
assert.isTrue(startSpy.notCalled);
mockTouchEnd('1');
assert.isTrue(startSpy.notCalled);
});
test('Pressing a button does not play a DTMF tone', function() {
var startToneSpy =
this.sinon.spy(MockNavigatorMozTelephony, 'startTone');
var stopToneSpy =
this.sinon.spy(MockNavigatorMozTelephony, 'stopTone');
mockTouchStart('1');
assert.isTrue(stopToneSpy.notCalled);
assert.isTrue(startToneSpy.notCalled);
mockTouchEnd('1');
});
});
suite('Keypad vibration', function() {
setup(function() {
this.sinon.spy(navigator, 'vibrate');
subject._observePreferences();
});
test('vibrates if setting is set', function() {
MockSettingsListener.mCallbacks['keyboard.vibration'](true);
mockTouchStart('1');
sinon.assert.calledWith(navigator.vibrate, 50);
});
test('does not vibrate if setting is not set', function() {
MockSettingsListener.mCallbacks['keyboard.vibration'](false);
mockTouchStart('1');
sinon.assert.notCalled(navigator.vibrate);
});
});
suite('During a call', function() {
var mockCall;
var mockHC;
var phoneNumber;
suiteSetup(function() {
realMozTelephony = navigator.mozTelephony;
navigator.mozTelephony = MockNavigatorMozTelephony;
});
suiteTeardown(function() {
MockNavigatorMozTelephony.mSuiteTeardown();
navigator.mozTelephony = realMozTelephony;
});
setup(function() {
phoneNumber = '12334';
mockCall = new MockCall(phoneNumber, 'connected', 0);
MockNavigatorMozTelephony.active = mockCall;
mockHC = telephonyAddCall.call(this, mockCall);
MockCallsHandler.mActiveCall = mockHC;
MockSettingsListener.mCallbacks['phone.ring.keypad'](true);
this.sinon.stub(document, 'elementFromPoint');
subject.init(/* oncall */ true);
subject.render('oncall');
});
teardown(function() {
subject.init(/* oncall */ false);
});
test('Disable abbreviated dialing codes', function() {
this.sinon.spy(KeypadManager, '_getSpeedDialNumber');
mockTypeNumber('1#');
sinon.assert.notCalled(KeypadManager._getSpeedDialNumber);
});
suite('Audible and DTMF tones', function() {
test('Pressing a button during a call plays a long tone', function() {
var startSpy = this.sinon.spy(MockTonePlayer, 'start');
mockTouchStart('1');
assert.isTrue(startSpy.calledWith(gTonesFrequencies['1'], false));
mockTouchEnd('1');
});
test('Short tones are enabled via prefs', function() {
var startSpy = this.sinon.spy(MockTonePlayer, 'start');
MockSettingsListener.mCallbacks['phone.dtmf.type']('short');
mockTouchStart('1');
assert.isTrue(startSpy.calledWith(gTonesFrequencies['1'], true));
mockTouchEnd('1');
});
test('Pressing a button during a call plays a DTMF tone', function() {
this.sinon.spy(MockNavigatorMozTelephony, 'startTone');
this.sinon.spy(MockNavigatorMozTelephony, 'stopTone');
mockTouchStart('1');
sinon.assert.calledWith(MockNavigatorMozTelephony.stopTone, 0);
sinon.assert.calledWith(MockNavigatorMozTelephony.startTone, '1', 0);
mockTouchEnd('1');
sinon.assert.calledTwice(MockNavigatorMozTelephony.stopTone);
});
test('Long DTMF tones stop when leaving the button', function() {
this.sinon.spy(MockNavigatorMozTelephony, 'startTone');
this.sinon.spy(MockNavigatorMozTelephony, 'stopTone');
MockSettingsListener.mCallbacks['phone.dtmf.type']('long');
mockTouchStart('1');
sinon.assert.calledWith(MockNavigatorMozTelephony.stopTone, 0);
sinon.assert.calledWith(MockNavigatorMozTelephony.startTone, '1', 0);
document.elementFromPoint.returns({ dataset: { value: '2' }});
mockTouchMove(0, 0);
sinon.assert.calledTwice(MockNavigatorMozTelephony.stopTone);
});
test('Short DTMF tones stop after 120ms', function() {
this.sinon.spy(MockNavigatorMozTelephony, 'startTone');
this.sinon.spy(MockNavigatorMozTelephony, 'stopTone');
MockSettingsListener.mCallbacks['phone.dtmf.type']('short');
mockTouchStart('1');
this.sinon.clock.tick(119);
sinon.assert.calledWith(MockNavigatorMozTelephony.stopTone, 0);
sinon.assert.calledOnce(MockNavigatorMozTelephony.startTone, '1', 0);
this.sinon.clock.tick(1);
sinon.assert.calledTwice(MockNavigatorMozTelephony.stopTone);
});
});
suite('then during a conference group', function() {
suiteSetup(function() {
MockCallsHandler.mActiveCall = null;
MockNavigatorMozTelephony.conferenceGroup.calls =
MockNavigatorMozTelephony.calls;
MockNavigatorMozTelephony.active =
MockNavigatorMozTelephony.conferenceGroup;
});
test('should not fail while typing', function() {
this.sinon.spy(MockNavigatorMozTelephony, 'startTone');
this.sinon.spy(MockNavigatorMozTelephony, 'stopTone');
mockTouchStart('1');
sinon.assert.calledWith(MockNavigatorMozTelephony.stopTone, 0);
sinon.assert.calledWith(MockNavigatorMozTelephony.startTone, '1', 0);
this.sinon.clock.tick();
assert.ok(true, 'got here');
mockTouchEnd('1');
sinon.assert.calledWith(MockNavigatorMozTelephony.stopTone, 0);
});
test('should not fail when restoring infos', function() {
subject.restorePhoneNumber();
subject.restoreAdditionalContactInfo();
assert.ok(true, 'got here');
});
});
test('Dialer is not limited to 50 digits while on a call', function() {
var digits = '11111111112222222222333333333344444444445555555555' +
'6666666666';
mockTypeNumber(digits);
assert.equal(subject._phoneNumber, digits);
});
test('Should return active call phone number', function() {
assert.equal(subject.phoneNumber(), phoneNumber);
});
});
suite('<<pause>> hotkey when long pressing *', function() {
var testNumberAfterLongPress = function(initialNumber, pressTime,
expectedNumber) {
subject._phoneNumber = initialNumber;
mockTouchStart('*', false);
this.sinon.clock.tick(pressTime);
mockTouchEnd('*');
assert.equal(KeypadManager.phoneNumber(), expectedNumber);
};
setup(function() {
testNumberAfterLongPress = testNumberAfterLongPress.bind(this);
});
test('it should output "," and remove "*"', function() {
testNumberAfterLongPress('12345', 1000, '12345,');
});
test('if it is the first symbol, it should not be added', function() {
testNumberAfterLongPress('', 1000, '');
});
test('if it is not long enough it should output "*"', function() {
testNumberAfterLongPress('123', 200, '123*');
});
});
suite('voiceMail hotkey', function() {
var fakeVoicemail;
suiteSetup(function() {
realMozActivity = window.MozActivity;
window.MozActivity = MockMozActivity;
});
suiteTeardown(function() {
window.MozActivity = realMozActivity;
});
setup(function() {
fakeVoicemail = '888';
KeypadManager._phoneNumber = '';
this.sinon.spy(CallHandler, 'call');
MockMozActivity.mSetup();
});
teardown(function() {
MockMozActivity.mTeardown();
});
var shouldRemove1FromPhoneNumber = function() {
assert.equal(KeypadManager.phoneNumber(), '');
};
var shouldOpenSettingsAppWithMozActivity = function() {
var activitySpy = this.sinon.spy(window, 'MozActivity');
subject.showVoicemailSettings();
sinon.assert.calledWithNew(activitySpy);
sinon.assert.calledOnce(activitySpy);
assert.deepEqual(activitySpy.firstCall.args, [{
name: 'configure',
data: {
target: 'device',
section: 'call'
}
}]);
};
suite('SingleSIM', function() {
setup(function() {
navigator.mozIccManager.iccIds[0] = 0;
MockNavigatorSettings.mSettings['ril.iccInfo.mbdn'] = fakeVoicemail;
mockLongPress('1', this.sinon.clock);
MockNavigatorSettings.mReplyToRequests();
});
test('should call voicemail directly', function() {
sinon.assert.calledWith(CallHandler.call, fakeVoicemail, 0);
});
test('should remove 1 from phone number', shouldRemove1FromPhoneNumber);
test('should display an error if no voicemail number is set',
function() {
var showSpy = this.sinon.spy(CustomDialog, 'show');
MockNavigatorSettings.mSettings['ril.iccInfo.mbdn'] = '';
mockLongPress('1', this.sinon.clock);
MockNavigatorSettings.mReplyToRequests();
var expectedVoicemailDialog = {
title: 'voicemailNoNumberTitle',
text: 'voicemailNoNumberText',
confirm: {
title: 'voicemailNoNumberSettings',
recommend: true,
callback: subject.showVoicemailSettings
},
cancel: {
title: 'voicemailNoNumberCancel',
callback: subject._hideNoVoicemailDialog
}
};
sinon.assert.calledWith(showSpy,
expectedVoicemailDialog.title, expectedVoicemailDialog.text,
expectedVoicemailDialog.cancel, expectedVoicemailDialog.confirm);
});
test('should open settings app with MozActivity when no voicemail set',
shouldOpenSettingsAppWithMozActivity);
});
suite('DualSIM', function() {
var fakeVoicemail2 = '666';
var simPicker;
setup(function(done) {
navigator.mozIccManager.iccIds[0] = 0;
navigator.mozIccManager.iccIds[1] = 1;
MockNavigatorSettings.mSettings['ril.iccInfo.mbdn'] = [
fakeVoicemail, fakeVoicemail2];
MockNavigatorSettings.mSettings['ril.voicemail.defaultServiceId'] = 1;
simPicker = document.getElementById('sim-picker');
this.sinon.spy(simPicker, 'getOrPick');
mockLongPress('1', this.sinon.clock);
MockNavigatorSettings.mReplyToRequests();
// Artificially delay setup because getOrPick gets called in
// l10n.formatValue which is a Promise
Promise.resolve().then(done, done);
});
test('should show the SIM picker for favorite SIM', function() {
sinon.assert.calledWith(simPicker.getOrPick, 1, 'voiceMail');
});
test('should call voicemail for SIM1', function() {
simPicker.getOrPick.yield(0);
MockNavigatorSettings.mReplyToRequests();
sinon.assert.calledWith(CallHandler.call, fakeVoicemail, 0);
});
test('should call voicemail for SIM2', function() {
simPicker.getOrPick.yield(1);
MockNavigatorSettings.mReplyToRequests();
sinon.assert.calledWith(CallHandler.call, fakeVoicemail2, 1);
});
test('should remove 1 from phone number', shouldRemove1FromPhoneNumber);
test('should open settings app with MozActivity when no voicemail set',
shouldOpenSettingsAppWithMozActivity);
});
test('pressing less than 400ms should not call', function() {
navigator.mozIccManager.iccIds[0] = 0;
MockNavigatorSettings.mSettings['ril.iccInfo.mbdn'] = fakeVoicemail;
mockLongPress('1', this.sinon.clock, 399);
MockNavigatorSettings.mReplyToRequests();
sinon.assert.notCalled(CallHandler.call);
});
test('pressing after another digit should not call', function() {
navigator.mozIccManager.iccIds[0] = 0;
MockNavigatorSettings.mSettings['ril.iccInfo.mbdn'] = fakeVoicemail;
mockLongPress('2', this.sinon.clock);
MockNavigatorSettings.mReplyToRequests();
mockLongPress('1', this.sinon.clock);
MockNavigatorSettings.mReplyToRequests();
sinon.assert.notCalled(CallHandler.call);
});
});
suite('Speed dial', function() {
var speedDialNum = '1#';
test(speedDialNum + ' is a speed dial number', function() {
assert.isTrue(subject._isSpeedDialNumber(speedDialNum));
});
test('123 is not a speed dial number', function() {
assert.isFalse(subject._isSpeedDialNumber('123'));
});
test('*#31# is not a speed dial number', function() {
assert.isFalse(subject._isSpeedDialNumber('*#31#'));
});
suite('Getting a speed dial number', function() {
var speedDialIndex = '1';
var numbers = [ '123', '456' ];
suiteSetup(function() {
navigator.mozIccManager.adnContacts = [
{
id: numbers[1],
tel: [ { value: numbers[1] } ]
},
{
id: numbers[0],
tel: [ { value: numbers[0] } ]
}
];
});
setup(function() {
this.sinon.spy(ConfirmDialog, 'show');
this.sinon.spy(ConfirmDialog, 'hide');
});
test('The overlay is displayed and then hidden', function(done) {
subject._getSimContactsList(0).then(function() {
sinon.assert.calledOnce(ConfirmDialog.show);
sinon.assert.calledWith(ConfirmDialog.show, 'loadingSimContacts');
sinon.assert.calledOnce(ConfirmDialog.hide);
}).then(done, done);
});
test('Cancelling the overlay works', function(done) {
navigator.mozIccManager.async = true;
var p = subject._getSimContactsList(0);
ConfirmDialog.executeNo();
p.then(function() {
assert.ok(false, 'Should not succeed');
}, function() {
sinon.assert.calledOnce(ConfirmDialog.show);
sinon.assert.calledOnce(ConfirmDialog.hide);
}).then(done, done);
navigator.mozIccManager.async = false;
});
test('The contacts are returned sorted by ID', function(done) {
subject._getSimContactsList(0).then(function(contacts) {
assert.equal(contacts[0].id, numbers[0]);
assert.equal(contacts[1].id, numbers[1]);
}).then(done, done);
});
test('The proper number is returned', function(done) {
subject._getSpeedDialNumber(speedDialIndex).then(function(number) {
assert.equal(number, numbers[0]);
}).then(done, done);
});
test('0# is ignored', function(done) {
subject._getSpeedDialNumber('0').then(function(number) {
assert.ok(false, 'the promise should be rejected');
}, function(error) {
assert.equal(error, 'noContactsWereFound');
}).then(done, done);
});
test('The SIM picker is used when there is no default SIM',
function(done) {
var simPicker = document.getElementById('sim-picker');
navigator.mozIccManager.iccIds[0] = 0;
navigator.mozIccManager.iccIds[1] = 1;
this.sinon.stub(SimSettingsHelper, 'getCardIndexFrom');
this.sinon.spy(simPicker, 'getOrPick');
var p = subject._getSpeedDialNumber(speedDialIndex);
SimSettingsHelper.getCardIndexFrom
.yield(SimSettingsHelper.ALWAYS_ASK_OPTION_VALUE);
simPicker.getOrPick.yield(0);
p.then(function(number) {
sinon.assert.calledOnce(simPicker.getOrPick);
assert.equal(number, numbers[0]);
}).then(done, done);
});
test('An error message is shown when no SIM card is present',
function(done) {
this.sinon.stub(MockIccManager.prototype, 'getIccById').returns(null);
subject._getSpeedDialNumber(speedDialIndex).then(function() {
assert.isTrue(false, 'The promise should not be resolved');
}, function(error) {
assert.equal(error, 'noSimCardToLoadContactsFrom');
}).then(done, done);
});
});
});
});
suite('hangUpCallFromKeypad', function() {
/* XXX: We don't import the full mock here because it still lives in the
* dialer sources and there's no point in moving it just for this test.
* This will go away as soon as the dialer and keypad are reunited. */
setup(function() {
window.CallScreen = { hideKeypad: this.sinon.stub() };
});
teardown(function() {
delete window.CallScreen;
});
test('hide the keypad before hanging up', function() {
this.sinon.spy(MockCallsHandler, 'end');
subject.hangUpCallFromKeypad();
CallScreen.hideKeypad.calledBefore(MockCallsHandler.end);
});
});
suite('Initializing MultiSimActionButton', function() {
var callBarCallActionButton;
var addEventListenerSpy;
setup(function() {
callBarCallActionButton =
document.getElementById('keypad-callbar-call-action');
addEventListenerSpy = this.sinon.spy(callBarCallActionButton,
'addEventListener');
subject.init(/* oncall */ false);
});
test('Should initialize MultiSimActionButton', function() {
assert.isTrue(MockMultiSimActionButtonSingleton.mIsInitialized);
});
test('Should pass a valid phone number getter', function() {
subject._phoneNumber = '1111111';
assert.equal(subject._phoneNumber,
MockMultiSimActionButtonSingleton._phoneNumberGetter());
});
test('Should add the first click event handler to the button', function() {
assert.equal(addEventListenerSpy.firstCall.args[0], 'click');
assert.equal(addEventListenerSpy.firstCall.args[1],
MockMultiSimActionButtonSingleton._click);
});
});
});

View file

@ -0,0 +1,707 @@
/* globals CALL_BARRING_STATUS_MMI_CODE,CALL_WAITING_STATUS_MMI_CODE,
MmiManager, MockMmiUI, MockMobileconnection, MockMobileOperator,
MockNavigatormozApps, MockNavigatorMozIccManager,
MockNavigatorMozMobileConnections, MockNavigatorMozTelephony,
MocksHelper, NotificationHelper, MockL10n */
'use strict';
require('/dialer/js/mmi.js');
require('/dialer/test/unit/mock_mmi_ui.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_mobile_operator.js');
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
require('/shared/test/unit/mocks/mock_navigator_moz_icc_manager.js');
require('/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js');
require('/shared/test/unit/mocks/mock_navigator_moz_telephony.js');
require('/shared/test/unit/mocks/mock_notification.js');
require('/shared/test/unit/mocks/mock_notification_helper.js');
require('/shared/test/unit/mocks/mock_l10n.js');
const SUCCESS_MMI_NO_MSG = 'sucess_mmi_no_msg';
const FAILED_MMI_NO_MSG = 'failed_mmi_no_msg';
const SUCCESS_MMI_MSG = 'success_mmi_msg';
const FAILED_MMI_MSG = 'failed_mmi_msg';
const MMI_MSG = 'mmi_msg';
const ICC_SERVICE_CLASS_VOICE = (1 << 0);
const ICC_SERVICE_CLASS_DATA = (1 << 1);
const ICC_SERVICE_CLASS_FAX = (1 << 2);
const ICC_SERVICE_CLASS_SMS = (1 << 3);
const ICC_SERVICE_CLASS_DATA_SYNC = (1 << 4);
const ICC_SERVICE_CLASS_DATA_ASYNC = (1 << 5);
const ICC_SERVICE_CLASS_PACKET = (1 << 6);
const ICC_SERVICE_CLASS_PAD = (1 << 7);
const EXPECTED_PHONE = '+34666222111';
var mocksHelperForMMI = new MocksHelper([
'LazyLoader',
'MmiUI',
'MobileOperator',
'NavigatorMozIccManager',
'NavigatorMozMobileConnections',
'NavigatorMozTelephony',
'Notification',
'NotificationHelper'
]).init();
suite('dialer/mmi', function() {
var realMozApps;
var realMozIccManager;
var realMobileConnections;
var realMozTelephony;
var mobileConn;
var realL10n;
var _;
mocksHelperForMMI.attachTestHelpers();
suiteSetup(function() {
realMozApps = navigator.mozApps;
navigator.mozApps = MockNavigatormozApps;
realMozIccManager = window.navigator.mozIccManager;
window.navigator.mozIccManager = MockNavigatorMozIccManager;
realMozTelephony = window.navigator.mozTelephony;
window.navigator.mozTelephony = MockNavigatorMozTelephony;
realMobileConnections = navigator.mozMobileConnections;
navigator.mozMobileConnections = MockNavigatorMozMobileConnections;
realMozTelephony = navigator.mozTelephony;
navigator.mozTelephony = MockNavigatorMozTelephony;
realL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
_ = MockL10n.get;
/* Replace the default mock connection with our own specialized version
* tailored for this suite of tests. */
mobileConn = new MockMobileconnection();
});
suiteTeardown(function() {
MockNavigatorMozTelephony.mSuiteTeardown();
navigator.mozApps = realMozApps;
navigator.mozIccManager = realMozIccManager;
navigator.mozTelephony = realMozTelephony;
navigator.mozMobileConnections = realMobileConnections;
navigator.mozTelephony = realMozTelephony;
navigator.mozL10n = realL10n;
});
setup(function() {
MockNavigatorMozMobileConnections.mRemoveMobileConnection(0);
MockNavigatorMozMobileConnections.mAddMobileConnection(mobileConn, 0);
this.sinon.spy(MockMmiUI, 'success');
this.sinon.spy(MockMmiUI, 'error');
this.sinon.spy(MockMmiUI, 'received');
});
teardown(function() {
MockNavigatorMozIccManager.mTeardown();
MockNavigatorMozMobileConnections.mTeardown();
MockNavigatorMozTelephony.mTeardown();
mobileConn.mTeardown();
MmiManager._conn = null;
});
suite('Successfully send mmi message with result', function() {
setup(function(done) {
MmiManager.handleDialing(mobileConn, SUCCESS_MMI_MSG, Promise.resolve({
success: true,
serviceCode: 'scFoo',
statusMessage: SUCCESS_MMI_MSG
})).then(done, done);
});
test('the message is populated correctly', function() {
sinon.assert.calledWith(MockMmiUI.success, SUCCESS_MMI_MSG, 'scFoo');
});
});
suite('Successfully send mmi message no result', function() {
setup(function(done) {
MmiManager.handleDialing(mobileConn, SUCCESS_MMI_NO_MSG, Promise.resolve({
success: true,
serviceCode: 'scFoo',
statusMessage: null
})).then(done, done);
});
test('check that a generic message is shown', function() {
sinon.assert.calledWith(MockMmiUI.success, null, 'scFoo');
});
});
suite('Error sending mmi message with result', function() {
setup(function(done) {
MmiManager.handleDialing(mobileConn, FAILED_MMI_MSG, Promise.resolve({
success: false,
serviceCode: 'scFoo',
statusMessage: FAILED_MMI_MSG
})).then(done, done);
});
test('the cause of failure is shown', function() {
sinon.assert.calledWith(MockMmiUI.error, FAILED_MMI_MSG, 'scFoo');
});
});
suite('Error sending mmi message no result', function() {
setup(function(done) {
MmiManager.handleDialing(mobileConn, FAILED_MMI_NO_MSG, Promise.resolve({
success: false,
serviceCode: 'scFoo',
statusMessage: null
})).then(done, done);
});
test('a generic error message is shown', function() {
sinon.assert.calledWith(MockMmiUI.error, 'GenericFailure', 'scFoo');
});
});
suite('Network initiated messages', function() {
setup(function() {
MockMobileOperator.mOperator = 'fake_carrier';
MockNavigatorMozIccManager.addIcc('0', { 'cardState' : 'ready' });
});
test('should handle network initiated messages properly', function(done) {
MmiManager.handleMMIReceived(MMI_MSG, {}, 0).then(function() {
done(function checks() {
sinon.assert.calledWith(MockMmiUI.received, {}, MMI_MSG,
MockMobileOperator.mOperator);
});
});
});
test('the notification is populated correctly for one SIM', function(done) {
this.sinon.spy(NotificationHelper, 'send');
this.sinon.spy(MmiManager, '_');
var promise = MmiManager.sendNotification(MMI_MSG, 0);
MockNavigatormozApps.mTriggerLastRequestSuccess();
promise.then(function() {
sinon.assert.calledOnce(NotificationHelper.send);
sinon.assert.calledWithMatch(NotificationHelper.send, 'fake_carrier', {
body: MMI_MSG,
icon: 'sms/dialer?ussdMessage=1&cardIndex=0'
});
sinon.assert.notCalled(MmiManager._);
}).then(done, done);
});
test('the notification is populated correctly for the second SIM',
function(done) {
this.sinon.spy(NotificationHelper, 'send');
this.sinon.spy(MmiManager, '_');
MockNavigatorMozIccManager.addIcc('1', { 'cardState' : 'ready' });
var promise = MmiManager.sendNotification(MMI_MSG, 1);
MockNavigatormozApps.mTriggerLastRequestSuccess();
promise.then(function() {
sinon.assert.calledOnce(NotificationHelper.send);
sinon.assert.calledWithMatch(NotificationHelper.send,
MockL10n._stringify('mmi-notification-title-with-sim', {
'sim': MockL10n._stringify('sim-number', {n: 2}),
'title': 'fake_carrier'
}),
{
body: MMI_MSG,
icon: 'sms/dialer?ussdMessage=1&cardIndex=1'
}
);
sinon.assert.calledTwice(MmiManager._);
}).then(done, done);
});
});
suite('Mmi received with message and session active', function() {
setup(function(done) {
MmiManager.handleMMIReceived(MMI_MSG, {}, 0).then(done, done);
});
test('the message is shown and a session is provided', function() {
sinon.assert.calledWith(MockMmiUI.received, {}, MMI_MSG);
});
});
suite('Mmi received with message and session ended', function() {
setup(function(done) {
MmiManager.handleMMIReceived(MMI_MSG, null, 0).then(done, done);
});
test('the message is shown and session is null', function() {
sinon.assert.calledWith(MockMmiUI.received, null, MMI_MSG);
});
});
suite('Mmi received with no message and an active session', function() {
setup(function(done) {
MmiManager.handleMMIReceived(null, {}, 0).then(done, done);
});
test('no message was received', function() {
sinon.assert.notCalled(MockMmiUI.received);
});
});
suite('Mmi received with no message and session ended', function() {
test('nothing is shown when message is empty', function(done) {
MmiManager.handleMMIReceived('', true, 0).then(function() {
done(function checks() {
sinon.assert.notCalled(MockMmiUI.received);
});
}, function() {
done(function() {
assert.isTrue(false, 'Should not reject the promise');
});
});
});
test('nothing is shown when message is null', function(done) {
MmiManager.handleMMIReceived(null, true, 0).then(function() {
done(function checks() {
sinon.assert.notCalled(MockMmiUI.received);
});
}, function() {
done(function() {
assert.isTrue(false, 'Should not reject the promise');
});
});
});
});
suite('Mmi message reply via UI', function() {
setup(function(done) {
MmiManager._conn = mobileConn;
MmiManager._session = {
send: function mockSend(message) {
return MmiManager.handleDialing(mobileConn, SUCCESS_MMI_MSG,
Promise.resolve({
success: true,
serviceCode: 'scFoo',
statusMessage: SUCCESS_MMI_MSG
})
);
}
};
MmiManager.reply(SUCCESS_MMI_MSG).then(done, done);
});
teardown(function() {
MmiManager._session = null;
});
test('the message is shown', function() {
sinon.assert.calledWith(MockMmiUI.success, SUCCESS_MMI_MSG, 'scFoo');
});
});
suite('Mmi message reply via UI with multiple connections', function() {
var simNum = 2;
setup(function(done) {
// Make this look like a DSDS setup
var conn = new MockMobileconnection();
MockNavigatorMozMobileConnections.mAddMobileConnection(conn, simNum - 1);
MockNavigatorMozIccManager.addIcc('0', { 'cardState' : 'ready' });
MockNavigatorMozIccManager.addIcc('1', { 'cardState' : 'ready' });
MmiManager._conn = conn;
MmiManager._session = {
send: function mockSend(message) {
return MmiManager.handleDialing(mobileConn, SUCCESS_MMI_MSG,
Promise.resolve({
success: true,
serviceCode: 'scFoo',
statusMessage: SUCCESS_MMI_MSG
})
);
}
};
MmiManager.reply(SUCCESS_MMI_MSG).then(done, done);
});
test('the message is shown', function() {
sinon.assert.calledWithMatch(MockMmiUI.success, SUCCESS_MMI_MSG,
/mmi-notification-title-with-sim.*sim-number.*1/);
});
});
suite('DSDS operation', function() {
var simNum = 2;
setup(function(done) {
// Make this look like a DSDS setup
var conn = new MockMobileconnection();
MockNavigatorMozMobileConnections.mAddMobileConnection(conn, simNum - 1);
MockNavigatorMozIccManager.addIcc('0', { 'cardState' : 'ready' });
MockNavigatorMozIccManager.addIcc('1', { 'cardState' : 'ready' });
MmiManager.handleMMIReceived(MMI_MSG, null, 1).then(done, done);
});
suite('Receiving a message', function() {
test('the display is populated and the session has ended', function() {
sinon.assert.calledWithMatch(MockMmiUI.received, null, MMI_MSG,
/mmi-notification-title-with-sim.*sim-number.*2/);
});
});
[0, 1].forEach(function(cardIndex) {
suite('Sending a message on SIM' + cardIndex, function() {
suite('Successfully with result', function() {
setup(function(done) {
MmiManager.handleDialing(
MockNavigatorMozMobileConnections[cardIndex],
SUCCESS_MMI_MSG,
Promise.resolve({
success: true,
serviceCode: 'scFoo',
statusMessage: SUCCESS_MMI_MSG
})
).then(done, done);
});
test('the display is populated and the session has ended',
function() {
sinon.assert.calledWithMatch(MockMmiUI.success, SUCCESS_MMI_MSG,
new RegExp('mmi-notification-title-with-sim.*sim-number.*' +
(cardIndex + 1)));
}
);
});
suite('Successfully with no result', function() {
setup(function(done) {
MmiManager.handleDialing(
MockNavigatorMozMobileConnections[cardIndex],
SUCCESS_MMI_NO_MSG, Promise.resolve({
success: true,
serviceCode: 'scFoo',
statusMessage: null
})
).then(done, done);
});
test('the display is populated and the session has ended',
function() {
sinon.assert.calledWithMatch(MockMmiUI.success, null,
new RegExp('mmi-notification-title-with-sim.*sim-number.*' +
(cardIndex + 1)));
}
);
});
suite('Error with result', function() {
setup(function(done) {
MmiManager.handleDialing(
MockNavigatorMozMobileConnections[cardIndex],
FAILED_MMI_MSG, Promise.resolve({
success: false,
serviceCode: 'scFoo',
statusMessage: FAILED_MMI_MSG
})
).then(done, done);
});
test('the error message is shown',
function() {
sinon.assert.calledWithMatch(MockMmiUI.error, FAILED_MMI_MSG,
new RegExp('mmi-notification-title-with-sim.*sim-number.*' +
(cardIndex + 1)));
}
);
});
suite('Error with no result', function() {
setup(function(done) {
MmiManager.handleDialing(
MockNavigatorMozMobileConnections[cardIndex],
FAILED_MMI_NO_MSG, Promise.resolve({
success: false,
serviceCode: 'scFoo',
statusMessage: null
})
).then(done, done);
});
test('a generic error message is shown',
function() {
sinon.assert.calledWithMatch(MockMmiUI.error, 'GenericFailure',
new RegExp('mmi-notification-title-with-sim.*sim-number.*' +
(cardIndex + 1)));
}
);
});
});
});
});
suite('Retrieving IMEI codes', function() {
test('for a single SIM device', function(done) {
var imei = '123';
/* The dial method returns a promise when sending an MMI code that
* resolves when the code has been successfully sent. However the
* response to the code will arrive only later by way of another promise
* returned as part of the return value of the first one. To mock this
* we have the first faked promise return an object holding a second
* faked promise, both of which resolve() immediately. */
this.sinon.stub(MockNavigatorMozTelephony, 'dial').returns(
Promise.resolve({
result: Promise.resolve({
success: true,
serviceCode: 'scImei',
statusMessage: imei
})
})
);
MmiManager.showImei().then(function() {
sinon.assert.calledWith(MockMmiUI.success, imei, 'scImei');
}).then(done, done);
});
test('for a multi-SIM device', function(done) {
var imeis = [ '123', '456' ];
var conn = new MockMobileconnection();
MockNavigatorMozMobileConnections.mAddMobileConnection(conn, 1);
this.sinon.stub(MockNavigatorMozTelephony, 'dial');
MockNavigatorMozTelephony.dial.onFirstCall().returns(
Promise.resolve({
result: Promise.resolve({
success: true,
serviceCode: 'scImei',
statusMessage: imeis[0]
})
})
);
MockNavigatorMozTelephony.dial.onSecondCall().returns(
Promise.resolve({
result: Promise.resolve({
success: true,
serviceCode: 'scImei',
statusMessage: imeis[1]
})
})
);
MmiManager.showImei().then(function() {
sinon.assert.calledWith(MockMmiUI.success, imeis.join('\n'), 'scImei');
}).then(done, done);
});
});
suite('Querying status for call barring or call waiting', function() {
setup(function() {
this.sinon.stub(MmiManager, '_').returnsArg(0);
});
test('should display correct message for barring enabled', function() {
MmiManager.notifySuccess({
serviceCode: 'scCallBarring',
statusMessage: 'smServiceEnabled'
}, CALL_BARRING_STATUS_MMI_CODE);
sinon.assert.calledWith(MockMmiUI.success, 'ServiceIsEnabled');
});
test('should display correct message for barring disabled', function() {
MmiManager.notifySuccess({
serviceCode: 'scCallBarring',
statusMessage: 'smServiceDisabled'
}, CALL_BARRING_STATUS_MMI_CODE);
sinon.assert.calledWith(MockMmiUI.success, 'ServiceIsDisabled');
});
test('should display correct message for waiting enabled', function() {
MmiManager.notifySuccess({
serviceCode: 'scCallWaiting',
statusMessage: 'smServiceEnabled'
}, CALL_WAITING_STATUS_MMI_CODE);
sinon.assert.calledWith(MockMmiUI.success, 'ServiceIsEnabled');
});
test('should display correct message for waiting disabled', function() {
MmiManager.notifySuccess({
serviceCode: 'scCallWaiting',
statusMessage: 'smServiceDisabled'
}, CALL_WAITING_STATUS_MMI_CODE);
sinon.assert.calledWith(MockMmiUI.success, 'ServiceIsDisabled');
});
});
suite('Call forwarding request via MMI.', function() {
var CALL_FORWARDING_STATUS_MMI_CODE = '*#23#';
var _spy;
var services = [
{ cl: ICC_SERVICE_CLASS_VOICE, str: 'voice' },
{ cl: ICC_SERVICE_CLASS_DATA, str: 'data' },
{ cl: ICC_SERVICE_CLASS_FAX, str: 'fax' },
{ cl: ICC_SERVICE_CLASS_SMS, str: 'sms' },
{ cl: ICC_SERVICE_CLASS_DATA_SYNC, str: 'sync' },
{ cl: ICC_SERVICE_CLASS_DATA_ASYNC, str: 'async' },
{ cl: ICC_SERVICE_CLASS_PACKET, str: 'packet' },
{ cl: ICC_SERVICE_CLASS_PAD, str: 'pad' },
];
/*
* Function used to check if a particular service class is present in an
* additional information field, not declared inline to please the linter.
*/
function matchAdditionalInformation(additionalInformation, id) {
return additionalInformation.find(function(info) {
return (info.serviceClass === id);
});
}
/*
* Helper used to check if the appropriate l10n strings for the specified
* call forwarding additional information object were called. The sole
* parameter is an array of additional information objects extracted from
* an USSD response message.
*/
function checkMessages(additionalInformation) {
services.forEach(function(service) {
var arg = {};
if (matchAdditionalInformation(additionalInformation, service.cl)) {
arg[service.str] = EXPECTED_PHONE;
} else {
arg[service.str] = 'call-forwarding-inactive';
}
sinon.assert.calledWith(_spy, 'call-forwarding-' + service.str, arg);
});
}
setup(function() {
_spy = this.sinon.spy(MmiManager, '_');
MmiManager._conn = mobileConn;
});
teardown(function() {
MmiManager._conn = null;
});
services.forEach(function(service) {
test('Check call forwarding rules, active ' + service.str,
function() {
var message = {
serviceCode: 'scCallForwarding',
statusMessage: 'smServiceInterrogated',
additionalInformation: [{
active: true,
number: EXPECTED_PHONE,
serviceClass: service.cl
}]
};
MmiManager.notifySuccess(message, CALL_FORWARDING_STATUS_MMI_CODE);
checkMessages(message.additionalInformation);
});
});
test('Check call forwarding rules, all inactive', function() {
var message = {
serviceCode: 'scCallForwarding',
statusMessage: 'smServiceInterrogated',
additionalInformation: [{
active: false
}]
};
MmiManager.notifySuccess(message, CALL_FORWARDING_STATUS_MMI_CODE);
checkMessages(message.additionalInformation);
});
test('Check call forwarding rules, two rules', function() {
var message = {
serviceCode: 'scCallForwarding',
statusMessage: 'smServiceInterrogated',
additionalInformation: [{
active: true,
number: EXPECTED_PHONE,
serviceClass: ICC_SERVICE_CLASS_VOICE
}, {
active: true,
number: EXPECTED_PHONE,
serviceClass: ICC_SERVICE_CLASS_DATA
}]
};
MmiManager.notifySuccess(message, CALL_FORWARDING_STATUS_MMI_CODE);
checkMessages(message.additionalInformation);
});
});
suite('SIM Card PIN change via MMI.', function() {
var SIM_PIN_CHANGE_MMI_CODE = '**04';
test('Check SIM Card PIN changes successful', function() {
var message = {
serviceCode: 'scPin',
statusMessage: 'smPinChanged',
success: 'true'
};
MmiManager.notifySuccess(message, SIM_PIN_CHANGE_MMI_CODE);
sinon.assert.calledWithMatch(MockMmiUI.success, 'smPinChanged', 'scPin');
});
test('Check error message for incorrect PIN is correct', function() {
var message = {
serviceCode: 'scPin',
statusMessage: 'emMmiErrorBadPin',
success: 'false',
additionalInformation: 2
};
MmiManager.notifyError(message);
sinon.assert.calledWithMatch(MockMmiUI.error,
_('emMmiErrorBadPin') + '\n' + _('emMmiErrorPinPukAttempts',
{ n: message.additionalInformation}),
'scPin');
});
test('Check error message for PUK locked due to max attempts', function() {
var message = {
serviceCode: 'scPin',
statusMessage: 'emMmiErrorNeedsPuk',
success: 'false'
};
MmiManager.notifyError(message);
sinon.assert.calledWithMatch(MockMmiUI.error, 'emMmiErrorNeedsPuk',
'scPin');
});
});
suite('Call control supplementary service commands', function() {
test('should cancel and hide the loading screen', function() {
this.sinon.spy(MockMmiUI, 'cancel');
MmiManager.notifySuccess({
serviceCode: 'scCall',
statusMessage: 'smCallControl'
}, 1);
sinon.assert.called(MockMmiUI.cancel);
sinon.assert.notCalled(MockMmiUI.success);
});
});
});

View file

@ -0,0 +1,187 @@
/* globals MmiUI, MocksHelper, MockMmiManager, MockL10n, MockNavigatormozApps */
'use strict';
require('/dialer/js/mmi_ui.js');
require('/dialer/test/unit/mock_mmi_manager.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mock_navigator_moz_apps.js');
var mocksHelperForMmiUi = new MocksHelper([
'MmiManager'
]).init();
suite('dialer/mmi UI', function() {
var realMozApps;
var realMozL10n;
mocksHelperForMmiUi.attachTestHelpers();
suiteSetup(function() {
realMozApps = navigator.mozApps;
navigator.mozApps = MockNavigatormozApps;
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
loadBodyHTML('/dialer/index.html');
MmiUI.init(function() {});
});
suiteTeardown(function() {
navigator.mozApps = realMozApps;
navigator.mozL10n = realMozL10n;
});
teardown(function() {
MockNavigatormozApps.mTeardown();
});
suite('MMI sending success >', function() {
var result = 'success';
var title = 'Success Title';
setup(function() {
this.sinon.spy(MmiUI, 'hideResponseForm');
this.sinon.spy(MmiUI, 'showMessage');
this.sinon.spy(MmiUI, 'showWindow');
});
test('UI is modified properly', function() {
MmiUI.success(result, title);
// Response form is hidden
sinon.assert.calledOnce(MmiUI.hideResponseForm);
assert.isTrue(MmiUI.sendNode.classList.contains('hide'));
assert.isFalse(MmiUI.mmiScreen.classList.contains('responseForm'));
// Message is shown
sinon.assert.calledOnce(MmiUI.showMessage);
sinon.assert.calledOnce(MmiUI.showWindow);
assert.isFalse(MmiUI.mmiScreen.hidden);
// Header is set with title
assert.equal(MmiUI.headerTitleNode.textContent, title);
// Message is correct
assert.equal(MmiUI.messageNode.textContent, result);
});
});
suite('MMI error >', function() {
var title = 'Error Title';
var error = 'Error Type';
setup(function() {
this.sinon.spy(MmiUI, 'hideResponseForm');
this.sinon.spy(MmiUI, 'showMessage');
this.sinon.spy(MmiUI, 'showWindow');
});
test('UI is modified properly', function() {
MmiUI.error(error, title);
// Response form is hidden
sinon.assert.calledOnce(MmiUI.hideResponseForm);
assert.isTrue(MmiUI.sendNode.classList.contains('hide'));
assert.isFalse(MmiUI.mmiScreen.classList.contains('responseForm'));
// Message is shown
sinon.assert.calledOnce(MmiUI.showMessage);
sinon.assert.calledOnce(MmiUI.showWindow);
assert.isFalse(MmiUI.mmiScreen.hidden);
// Header is set with title
assert.equal(MmiUI.headerTitleNode.textContent, title);
// Message is correct
assert.equal(MmiUI.messageNode.textContent, error);
});
});
suite('MMI received >', function() {
var message = 'received ui';
var title = 'Received UI Title';
setup(function() {
this.sinon.spy(MmiUI, 'hideResponseForm');
this.sinon.spy(MmiUI, 'showResponseForm');
this.sinon.spy(MmiUI, 'showMessage');
this.sinon.spy(MmiUI, 'showWindow');
});
test('UI is modified properly', function() {
MmiUI.received({}, message, title);
// Response form is shown
sinon.assert.calledOnce(MmiUI.showResponseForm);
assert.isFalse(MmiUI.sendNode.classList.contains('hide'));
assert.isTrue(MmiUI.mmiScreen.classList.contains('responseForm'));
// Message is shown
sinon.assert.calledOnce(MmiUI.showMessage);
sinon.assert.calledOnce(MmiUI.showWindow);
assert.isFalse(MmiUI.mmiScreen.hidden);
// Header is set with title
assert.equal(MmiUI.headerTitleNode.textContent, title);
// Message is correct
assert.equal(MmiUI.messageNode.textContent, message);
});
test('Dialer is shown', function() {
MmiUI.received({}, message, title);
MockNavigatormozApps.mTriggerLastRequestSuccess();
assert.equal(MockNavigatormozApps.mAppWasLaunchedWithEntryPoint,
'dialer');
});
test('UI is modified properly (session expired)', function() {
MmiUI.received(null, message, title);
// Response form is hidden
sinon.assert.calledOnce(MmiUI.hideResponseForm);
assert.isTrue(MmiUI.sendNode.classList.contains('hide'));
assert.isFalse(MmiUI.mmiScreen.classList.contains('responseForm'));
// Message is shown
sinon.assert.calledOnce(MmiUI.showMessage);
sinon.assert.calledOnce(MmiUI.showWindow);
assert.isFalse(MmiUI.mmiScreen.hidden);
// Header is set with title
assert.equal(MmiUI.headerTitleNode.textContent, title);
// Message is correct
assert.equal(MmiUI.messageNode.textContent, message);
});
});
suite('MMI loading >', function() {
test('UI is modified properly', function() {
MmiUI.loading();
// Show loading panel
assert.isFalse(MmiUI.loadingOverlay.classList.contains('hide'));
// Response disabled
assert.isTrue(MmiUI.responseTextNode.disabled);
// Reply disabled
assert.isTrue(MmiUI.sendNode.disabled);
});
});
suite('MMI screen close >', function() {
setup(function() {
var message = 'received ui';
var title = 'Received UI Title';
MmiUI.received({}, message, title);
});
test('Dialer is hidden and response is empty', function() {
this.sinon.spy(MockMmiManager, 'cancel');
MmiUI.closeWindow();
// Pending USSD session is cancelled.
sinon.assert.calledOnce(MockMmiManager.cancel);
// Message is hidden
assert.isTrue(MmiUI.mmiScreen.hidden);
// Response is empty
assert.equal(MmiUI.responseTextNode.value, '');
});
});
});

View file

@ -0,0 +1,7 @@
'use strict';
/* exported MockCallGroupMenu */
var MockCallGroupMenu = {
show: function(contactId, phoneNumber, day, type) {}
};

View file

@ -0,0 +1,12 @@
/* exported MockCallHandler */
'use strict';
var MockCallHandler = {
_lastCall: null,
init: function() {},
call: function call(number) {
this._lastCall = number;
}
};

View file

@ -0,0 +1,7 @@
'use strict';
/* exported MockCallInfo */
var MockCallInfo = {
show: function(number, day, type) {}
};

View file

@ -0,0 +1,20 @@
'use strict';
/* exported MockCallLog */
var MockCallLog = {
mCallLogEditModeButtonHidden: false,
init: function() {},
appendGroup: function(group) {
},
showEditModeButton: function() {
this.mCallLogEditModeButtonHidden = false;
},
hideEditModeButton: function() {
this.mCallLogEditModeButtonHidden = true;
},
mShutdown() {
this.mCallLogEditModeButtonHidden = false;
}
};

View file

@ -0,0 +1,76 @@
'use strict';
/* exported MockCallLogDBManager */
var MockCallLogDBManager = {
_calls: [],
_getGroupListCursor: 0,
// This is to emulate DB cursor for 'getGroupList'
value: null,
_getGroupListCallback: null,
add: function add(recentCall, callback) {
this._calls.push(recentCall);
var group = {};
group.number = recentCall.number;
if (callback) {
callback(group);
}
},
getGroupAtPosition: function getGroupAtPosition(
position, sortedBy, prev, type, callback) {
var found = false;
this._calls.forEach(function(call) {
if (call.type === type) {
found = true;
var group = {};
group.number = call.number;
callback(group);
return false;
}
});
if (!found && callback) {
callback();
}
},
getGroupList: function getGroupList(callback) {
this._getGroupListCursor = 0;
this._getGroupListCallback = callback;
this.continue();
},
deleteAll: function deleteAll(callback) {
this._calls = [];
callback();
},
updateGroupContactInfo: function(contact, matchingTel, callback) {
var callsMatched = 0;
this._calls.forEach(function(call) {
if (call.contact.matchingTel.value == matchingTel.value) {
call.contact = contact;
callsMatched++;
}
});
callback(callsMatched);
},
removeGroupContactInfo: function(contactId, group, callback) {
var callsMatched = 0;
for (var i=0; i < this._calls.length; i++) {
if (this._calls[i].contact.id == contactId) {
delete this._calls[i];
callsMatched++;
}
}
callback(callsMatched);
},
continue: function mcldm_continue() {
if (this._calls.length > this._getGroupListCursor) {
this.value = this._calls[this._getGroupListCursor];
this._getGroupListCursor++;
} else {
this.value = null;
}
if (this._getGroupListCallback != null) {
this._getGroupListCallback(this);
}
},
getGroup: function() {},
};

View file

@ -0,0 +1,90 @@
<li role="presentation" id="keyboard-view">
<a id="option-keypad" role="tab" aria-controls="keypad-panel" href="#keyboard-view" class="icon icon-keypad" data-l10n-id="keypad" aria-selected="true"></a>
<div id="keypad-panel" role="tabpanel" class="bb-tabpanel" aria-labelledby="option-keypad">
<div id="phone-number-view-container">
<div>
<input id="phone-number-view" type="text" class="phone-number-font font-light" readonly="readonly" data-l10n-id="phoneNumberInput">
<div id="fake-phone-number-view"></div>
</div>
<button role="key" id="keypad-delete" data-value="delete" data-l10n-id="keypadDelete">
</button>
</div>
</div>
<section id="suggestion-bar hide">
<div id="suggestion-count" class="more"></div>
<div class="contact-item">
<div class="name"></div>
<div>
<span class="tel-type"></span>
<span class="tel"><span class="matched"></span></span>
</div>
</div>
</section>
<article id="keyboard-container">
<section id="keypad" dir="ltr">
<div class="keypad-row">
<div class="keypad-key" role="key" data-value="1">
<span>1</span>
<em class="keypad-subicon voicemail" data-l10n-id="voiceMailKey"></em>
</div>
<div class="keypad-key" role="key" data-value="2">
<span>2</span>
<em>ABC</em>
</div>
<div class="keypad-key" role="key" data-value="3">
<span>3</span>
<em>DEF</em>
</div>
</div>
<div class="keypad-row">
<div class="keypad-key" role="key" data-value="4">
<span>4</span>
<em>GHI</em>
</div>
<div class="keypad-key" role="key" data-value="5">
<span>5</span>
<em>JKL</em>
</div>
<div class="keypad-key" role="key" data-value="6">
<span>6</span>
<em>MNO</em>
</div>
</div>
<div class="keypad-row">
<div class="keypad-key" role="key" data-value="7">
<span>7</span>
<em>PQRS</em>
</div>
<div class="keypad-key" role="key" data-value="8">
<span>8</span>
<em>TUV</em>
</div>
<div class="keypad-key" role="key" data-value="9">
<span>9</span>
<em>WXYZ</em>
</div>
</div>
<div class="keypad-row">
<div class="keypad-key" role="key" data-value="*" data-l10n-id="asterisk">
<span class="asterisk">*</span>
<em>’</em>
</div>
<div class="keypad-key" role="key" data-value="0">
<span>0</span>
<em class="font-size-plus">+</em>
</div>
<div class="keypad-key" role="key" data-type="dial" data-value="#" data-l10n-id="hashSign">
<div class="sharp">#</div>
</div>
</div>
</section>
<section id="keypad-callbar">
<button id="keypad-callbar-add-contact" data-value="add-contact" class="keypad-callbar-button icon-add-contact" data-l10n-id="dialerAddContact" disabled></button>
<button id="keypad-callbar-call-action" data-value="make-call" data-l10n-id="makeCall">
<div class="js-sim-indication"></div>
</button>
</section>
</article>
</div>
<gaia-sim-picker id="sim-picker"></gaia-sim-picker>
</li>

View file

@ -0,0 +1,12 @@
'use strict';
/* exported MockIccHelper */
var MockIccHelper = {
mCardState: 'ready',
addEventListener: function icch_addEventListener(event, handler) {},
get cardState() {
return this.mCardState;
}
};

View file

@ -0,0 +1,24 @@
'use strict';
var MockMmiManager = {
init: function(cb) {
if (typeof cb === 'function') {
cb();
}
},
send: function(message) {},
notifySuccess: function(evt) {},
notifyError: function(evt) {},
openUI: function() {},
handleMMIReceived: function(message, session, cardIndex) {},
handleRequest: function(conn, message, request) {},
sendNotification: function(message, cardIndex) {
return { then: function(callback) { callback(); } };
},
handleEvent: function(evt) {},
isImei: function() { return false; },
showImei: function() {},
cancel: function(){},
};
window.MockMmiManager = MockMmiManager;

View file

@ -0,0 +1,15 @@
/* exported MockMmiUI */
'use strict';
var MockMmiUI = {
init: function mmui_init() {},
success: function mmui_success(message, title) {},
error: function mmui_error(error, title) {},
received: function mmui_received(session, message, title) {},
loading: function mmui_loading() {},
reply: function mmui_reply() {},
closeWindow: function mmui_closeWindow() {},
cancel: function mmui_cancel() {},
close: function mmui_close() {}
};

View file

@ -0,0 +1,11 @@
'use strict';
(function(exports) {
var MockMozVoicemail = {
_number: null,
getNumber: function(serviceId) {
return this._number;
}
};
exports.MockMozVoicemail = MockMozVoicemail;
}(window));

View file

@ -0,0 +1,11 @@
/* exported MockNavbarManager */
(function(exports) {
'use strict';
var MockNavbarManager = {
init: function() {}
};
exports.MockNavbarManager = MockNavbarManager;
})(this);

View file

@ -0,0 +1,23 @@
'use strict';
/* exported MockNavigation */
var MockNavigation = {
_currentView: null,
get currentView() {
return this._currentView;
},
show: function(destination) {
this._currentView = destination;
},
showKeypad: function() {
this.show('keypad');
},
showCalllog: function() {
this.show('recents');
},
showContacts: function() {
this.show('contacts');
}
};

View file

@ -0,0 +1,7 @@
/* exported MockPhoneNumberActionMenu */
'use strict';
var MockPhoneNumberActionMenu = {
show: function() {}
};

View file

@ -0,0 +1,11 @@
/* exported MockSuggestionBar */
'use strict';
var MockSuggestionBar = {
handleEvent: function msb_handleEvent(event) {},
update: function msb_update(number) {},
clear: function msb_clear(isHardClear) {},
showOverlay: function msb_showOverlay() {},
hideOverlay: function msb_hideOverlay() {}
};

View file

@ -0,0 +1,173 @@
'use strict';
/* global MockAccessibilityHelper, MocksHelper, Navigation */
require('/dialer/test/unit/mock_call_log.js');
require('/shared/test/unit/mocks/mock_accessibility_helper.js');
require('/dialer/js/navigation.js');
var mocksHelperForNavigation = new MocksHelper([
'AccessibilityHelper',
'CallLog'
]).init();
suite('navigation bar', function() {
var domKeypadPanel;
var domRecentsPanel;
var domIframeContactsContainer;
var domContactsIframe;
var domOptionRecents;
var domOptionContacts;
var domOptionKeypad;
var domViews;
mocksHelperForNavigation.attachTestHelpers();
setup(function() {
domKeypadPanel = document.createElement('div');
domKeypadPanel.id = 'keypad-panel';
domKeypadPanel.style.visibility = 'hidden';
document.body.appendChild(domKeypadPanel);
domIframeContactsContainer = document.createElement('div');
domIframeContactsContainer.id = 'iframe-contacts-container';
domIframeContactsContainer.style.visibility = 'hidden';
document.body.appendChild(domIframeContactsContainer);
domContactsIframe = document.createElement('iframe');
domContactsIframe.id = 'iframe-contacts';
domIframeContactsContainer.appendChild(domContactsIframe);
domRecentsPanel = document.createElement('div');
domRecentsPanel.id = 'recents-panel';
domRecentsPanel.style.visibility = 'hidden';
document.body.appendChild(domRecentsPanel);
domViews = document.createElement('section');
domViews.id = 'views';
domOptionRecents = document.createElement('a');
domOptionRecents.id = 'option-recents';
domOptionRecents.dataset.destination = 'recents';
domViews.appendChild(domOptionRecents);
domOptionContacts = document.createElement('a');
domOptionContacts.id = 'option-contacts';
domOptionContacts.dataset.destination = 'contacts';
domViews.appendChild(domOptionContacts);
domOptionKeypad = document.createElement('a');
domOptionKeypad.id = 'option-keypad';
domOptionKeypad.dataset.destination = 'keypad';
domViews.appendChild(domOptionKeypad);
document.body.appendChild(domViews);
});
teardown(function() {
document.body.removeChild(domKeypadPanel);
document.body.removeChild(domRecentsPanel);
document.body.removeChild(domIframeContactsContainer);
document.body.removeChild(domViews);
});
suite('Navigation', function() {
suite('show()', function() {
setup(function() {
this.sinon.spy(MockAccessibilityHelper, 'setAriaSelected');
});
test('throws when given an invalid destination', function() {
try {
Navigation.show('unknown');
assert.isTrue(false, 'Should throw');
} catch (e) {
assert.instanceOf(e, Error);
}
});
test('shows the keypad', function() {
Navigation.showKeypad();
assert.equal(domKeypadPanel.style.visibility, 'visible');
assert.isTrue(
domOptionKeypad.classList.contains('toolbar-option-selected')
);
sinon.assert.calledWith(
MockAccessibilityHelper.setAriaSelected,
domOptionKeypad
);
});
test('shows the call log', function() {
Navigation.showCalllog();
assert.equal(domRecentsPanel.style.visibility, 'visible');
assert.isTrue(
domOptionRecents.classList.contains('toolbar-option-selected')
);
sinon.assert.calledWith(
MockAccessibilityHelper.setAriaSelected,
domOptionRecents
);
});
test('shows the contacts', function() {
var dummyIframe = document.createElement('iframe');
this.sinon.stub(document, 'createElement').returns(dummyIframe);
this.sinon.spy(domIframeContactsContainer, 'appendChild');
domIframeContactsContainer.removeChild(domContactsIframe);
Navigation.showContacts();
assert.equal(domIframeContactsContainer.style.visibility, 'visible');
assert.isTrue(
domOptionContacts.classList.contains('toolbar-option-selected')
);
sinon.assert.calledWith(
MockAccessibilityHelper.setAriaSelected,
domOptionContacts
);
sinon.assert.calledWith(document.createElement, 'iframe');
sinon.assert.calledWith(
domIframeContactsContainer.appendChild,
dummyIframe
);
assert.include(dummyIframe.src, '/contacts/index.html');
assert.equal(dummyIframe.id, 'iframe-contacts');
assert.isTrue(dummyIframe.hasAttribute('frameBorder'));
assert.equal(dummyIframe.getAttribute('frameBorder'), 'no');
assert.isTrue(dummyIframe.classList.contains('grid-wrapper'));
});
test('navigate between panels', function() {
Navigation.showKeypad();
Navigation.showCalllog();
assert.equal(domKeypadPanel.style.visibility, 'hidden');
assert.isFalse(
domOptionKeypad.classList.contains('toolbar-option-selected')
);
assert.equal(domRecentsPanel.style.visibility, 'visible');
assert.isTrue(
domOptionRecents.classList.contains('toolbar-option-selected')
);
});
});
suite('currentView', function() {
test('returns the current view', function() {
Navigation.showKeypad();
assert.equal(Navigation.currentView, 'keypad');
Navigation.showCalllog();
assert.equal(Navigation.currentView, 'recents');
});
});
});
});

View file

@ -0,0 +1,532 @@
/* globals LazyLoader, MockCallHandler, MockContacts, MockFbContacts,
MockL10n, MockNavigatorMozIccManager, MockUtils, MocksHelper,
SuggestionBar, SimSettingsHelper */
'use strict';
require('/shared/test/unit/mocks/mock_navigator_moz_icc_manager.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/dialer/test/unit/mock_call_handler.js');
// FIXME : This should be a mock
require('/shared/js/simple_phone_matcher.js');
require('/shared/test/unit/mocks/mock_fb_data_reader.js');
require('/shared/test/unit/mocks/dialer/mock_contacts.js');
require('/shared/test/unit/mocks/dialer/mock_keypad.js');
require('/shared/test/unit/mocks/dialer/mock_utils.js');
require('/shared/test/unit/mocks/mock_sim_settings_helper.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/dialer/js/suggestion_bar.js');
var mocksHelperForSuggestionBar = new MocksHelper([
'Contacts',
'LazyLoader',
'KeypadManager',
'CallHandler',
'SimSettingsHelper',
'Utils'
]).init();
suite('suggestion Bar', function() {
var realFbContacts;
var realMozIccManager;
var realMozL10n;
mocksHelperForSuggestionBar.attachTestHelpers();
suiteSetup(function() {
window.fb = window.fb || {};
realFbContacts = window.fb.contacts;
window.fb.contacts = MockFbContacts;
realMozIccManager = navigator.mozIccManager;
navigator.mozIccManager = MockNavigatorMozIccManager;
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
MockNavigatorMozIccManager.mTeardown();
});
var domSuggestionBar;
var domSuggestionCount;
var domOverlay;
var subject;
var mockResult1 = [{
id: '000000',
name: ['John'],
tel: [
{ type: 'mobile',
value: '111111111' },
{ type: 'my-custom-type',
value: '1234567890' }]
}];
var mockResult2 = [{
id: '000000',
name: ['John'],
tel: [
{ type: 'mobile',
value: '111111111' }]
},{
id: '000000',
name: ['Mary'],
tel: [
{ type: 'mobile',
value: '111122222' }]
}];
var mockResult3 = [{
id: '000000',
name: ['John'],
tel: [
{ type: 'mobile',
value: '12345678' },
{ type: 'home',
value: '12343210' }]
},{
id: '000000',
name: ['George'],
tel: [
{ type: 'mobile',
value: '12340000' }]
}];
var mockResultFb = [{
id: '000000',
name: ['William'],
tel: [
{ type: 'mobile',
value: '12349999' }
]
},
{
id: '000001',
name: ['Jack'],
tel: [
{ type: 'mobile',
value: '12341111' }
]
}];
setup(function() {
subject = SuggestionBar;
MockNavigatorMozIccManager.addIcc(0, {});
loadBodyHTML('/shared/elements/contacts/contact_in_overlay.html');
var suggestionItemTemplate = document.body.querySelector('template');
loadBodyHTML('/shared/elements/contacts/contact_list_overlay.html');
var suggestionOverlayTemplate = document.body.querySelector('template');
domSuggestionBar = document.createElement('section');
domSuggestionBar.id = 'suggestion-bar';
domSuggestionBar.classList.add('hide');
domSuggestionBar.innerHTML =
`<div id="suggestion-count" class="more"></div>
<div is="contact-in-overlay"
class="js-suggestion-item contact-item"></div>`;
document.body.appendChild(domSuggestionBar);
var suggestionItem = document.querySelector('.js-suggestion-item');
suggestionItem.innerHTML = '';
suggestionItem.appendChild(suggestionItemTemplate.content.cloneNode(true));
var domSuggestionItem = document.createElement('button');
domSuggestionItem.id = 'contact-in-overlay-template';
domSuggestionItem.setAttribute('role', 'button');
domSuggestionItem.setAttribute('is', 'contact-in-overlay');
domSuggestionItem.classList.add('js-suggestion-item', 'contact-item',
'bb-button');
domSuggestionItem.hidden = true;
document.body.appendChild(domSuggestionItem);
domSuggestionItem.innerHTML = '';
domSuggestionItem.appendChild(
suggestionItemTemplate.content.cloneNode(true));
domOverlay = document.createElement('form');
domOverlay.id = 'contact-list-overlay';
domOverlay.setAttribute('is', 'contact-list-overlay');
domOverlay.setAttribute('role', 'dialog');
domOverlay.dataset.type = 'action';
domOverlay.classList.add('overlay');
domOverlay.setAttribute('aria-hidden', 'true');
domOverlay.innerHTML = '';
domOverlay.appendChild(suggestionOverlayTemplate.content.cloneNode(true));
document.body.appendChild(domOverlay);
domSuggestionCount = domSuggestionBar.querySelector('#suggestion-count');
subject.overlay = domOverlay;
subject.bar = domSuggestionBar;
subject.countTag = document.getElementById('suggestion-count');
subject.list = document.getElementById('contact-list');
subject.overlayCancel =
document.getElementById('contact-list-overlay-cancel');
subject.init();
this.sinon.spy(MockL10n, 'setAttributes');
MockContacts.mTearDown();
MockFbContacts.mTeardown();
});
teardown(function() {
document.body.removeChild(domSuggestionBar);
document.body.removeChild(domOverlay);
MockNavigatorMozIccManager.mTeardown();
});
suiteTeardown(function() {
window.fb.contacts = realFbContacts;
navigator.mozL10n = realMozL10n;
navigator.mozIccManager = realMozIccManager;
});
var cloneMockContactResults = function(count) {
MockContacts.mResult = new Array(count);
for (var i = 0; i < MockContacts.mResult.length; i++) {
MockContacts.mResult[i] = mockResult1[0];
}
};
suite('Suggestion Bar', function() {
var tel;
var telType;
setup(function() {
tel = domSuggestionBar.querySelector('.js-tel');
telType = domSuggestionBar.querySelector('.js-tel-type');
});
test('#update suggestions by contact data - 1 data', function() {
var mockNumber = '1234567890';
var enteredNumber = '1234';
sinon.spy(telType, 'removeAttribute');
MockContacts.mResult = mockResult1;
subject.update(enteredNumber);
assert.equal(tel.textContent, mockNumber,
'should got number 1234567890 from mozContact');
sinon.assert.notCalled(MockL10n.setAttributes);
sinon.assert.calledWith(telType.removeAttribute, 'data-l10n-id');
assert.equal(telType.textContent, 'my-custom-type',
'should default to the type string when there is no ' +
'localization');
assert.isFalse(domSuggestionCount.classList.contains('more'),
'#suggestion-count shouldn\'t contain "more" style');
assert.isFalse(domSuggestionBar.classList.contains('hide'));
});
test('#update suggestions by contact data - 2 datas', function() {
var mockNumber = '111111111';
var enteredNumber = '1111';
this.sinon.stub(MockUtils, 'isPhoneType').returns(true);
MockContacts.mResult = mockResult2;
subject.update(enteredNumber);
assert.equal(tel.textContent, mockNumber,
'should got number 111111111 from mozContact');
sinon.assert.calledWith(MockL10n.setAttributes, telType, 'mobile');
assert.isTrue(domSuggestionCount.classList.contains('more'),
'#suggestion-count should contain "more" style');
assert.isFalse(domSuggestionBar.classList.contains('hide'));
});
test('#update suggestions by contact data - 0 local data - 1 FB data',
function() {
var mockNumber = '12349999';
var enteredNumber = '1234';
this.sinon.stub(MockUtils, 'isPhoneType').returns(true);
MockContacts.mResult = [];
MockFbContacts.mResult = mockResultFb.slice(0, 1);
subject.update(enteredNumber);
assert.equal(tel.textContent, mockNumber,
'should got number 12349999 from Facebook');
sinon.assert.calledWith(MockL10n.setAttributes, telType, 'mobile');
assert.isFalse(domSuggestionCount.classList.contains('more'),
'#suggestion-count should not contain "more" style');
assert.isFalse(domSuggestionBar.classList.contains('hide'));
assert.equal(SuggestionBar._contactList.length, 1,
'_contactList.length should be 1');
});
test('#update suggestions by contact data - 1 local data - 1 FB data',
function() {
var mockNumber = '1234567890';
var enteredNumber = '1234';
sinon.spy(telType, 'removeAttribute');
MockContacts.mResult = mockResult1;
MockFbContacts.mResult = mockResultFb.slice(0, 1);
subject.update(enteredNumber);
assert.equal(tel.textContent, mockNumber,
'should got number 1234567890 from mozContact');
sinon.assert.notCalled(MockL10n.setAttributes);
sinon.assert.calledWith(telType.removeAttribute, 'data-l10n-id');
assert.equal(telType.textContent, 'my-custom-type',
'should default to the type string when there is no ' +
'localization');
assert.isTrue(domSuggestionCount.classList.contains('more'),
'#suggestion-count should contain "more" style');
assert.isFalse(domSuggestionBar.classList.contains('hide'));
});
test('#update suggestions by contact data - 0 local data - 2 FB data',
function() {
var mockNumber = '12349999';
var enteredNumber = '1234';
this.sinon.stub(MockUtils, 'isPhoneType').returns(true);
MockContacts.mResult = [];
MockFbContacts.mResult = mockResultFb;
subject.update(enteredNumber);
assert.equal(tel.textContent, mockNumber,
'should got number 12349999 from Facebook');
sinon.assert.calledWith(MockL10n.setAttributes, telType, 'mobile');
assert.isTrue(domSuggestionCount.classList.contains('more'),
'#suggestion-count should contain "more" style');
assert.isFalse(domSuggestionBar.classList.contains('hide'));
});
test('#update suggestions by contact data - 50 local data - 0 FB data',
function() {
var enteredNumber = '1234';
cloneMockContactResults(50);
subject.update(enteredNumber);
assert.isFalse(domSuggestionBar.classList.contains('hide'));
});
test('#update suggestions by contact data - 51 local data - 0 FB data',
function() {
var enteredNumber = '1234';
cloneMockContactResults(51);
subject.update(enteredNumber);
assert.isTrue(domSuggestionBar.classList.contains('hide'));
});
test('#update suggestions by contact data - 50 local data - 1 FB data',
function() {
var enteredNumber = '1234';
cloneMockContactResults(50);
MockFbContacts.mResult = mockResultFb.slice(0, 1);
subject.update(enteredNumber);
assert.isTrue(domSuggestionBar.classList.contains('hide'));
});
suite('#clear suggestions', function() {
setup(function() {
var enteredNumber = '1234';
MockContacts.mResult = mockResult1;
subject.update(enteredNumber);
subject.clear();
});
test('should clear contents', function() {
assert.equal(tel.textContent, '');
});
test('should hide suggestionBar', function() {
assert.isTrue(domSuggestionBar.classList.contains('hide'));
});
test('should not try to localize anything', function() {
sinon.assert.notCalled(MockL10n.setAttributes);
});
});
suite('#update suggestions - exact match', function() {
var setupExactMatch = function() {
var enteredNumber = '1234567890';
MockContacts.mResult = mockResult1;
MockFbContacts.mResult = [];
subject.update(enteredNumber);
};
test('one SIM', function() {
var mockNumber = '1234567890';
setupExactMatch();
assert.isFalse(domSuggestionBar.classList.contains('hide'));
assert.equal(tel.textContent, mockNumber);
});
test('two SIMs', function() {
MockNavigatorMozIccManager.addIcc(1, {});
setupExactMatch();
assert.isTrue(domSuggestionBar.classList.contains('hide'));
});
});
});
suite('Suggestion List', function() {
suite('show overlay', function() {
var suggestions;
var getSuggestions = function() {
suggestions = Array.prototype.filter.call(subject.list.children,
function(element) {
return element.classList.contains('js-suggestion-item');
});
};
setup(function() {
MockContacts.mResult = mockResult2;
subject.update('1111');
this.sinon.spy(LazyLoader, 'load');
this.sinon.stub(MockUtils, 'isPhoneType').returns(true);
MockL10n.setAttributes.reset();
subject.showOverlay();
getSuggestions();
});
test('should load the overlay', function() {
sinon.assert.calledWith(LazyLoader.load, domOverlay);
});
test('overlay is displayed', function() {
assert.equal(subject.overlay.getAttribute('aria-hidden'), 'false');
assert.isTrue(subject.overlay.classList.contains('display'));
});
test('should have a cancel button as the last button', function() {
var buttons = subject.list.children;
var cancel = buttons[buttons.length - 1];
assert.equal(cancel.id, 'contact-list-overlay-cancel');
});
test('should have 2 suggestions', function() {
assert.equal(suggestions.length, 2);
});
test('should call mozL10n.setAttributes with correct arguments',
function() {
// _fillContacts() calls two more times
sinon.assert.calledThrice(MockL10n.setAttributes);
assert.equal(MockL10n.setAttributes.getCall(1).args[1], 'mobile');
assert.equal(MockL10n.setAttributes.getCall(2).args[1], 'mobile');
});
test('each match is displayed in the proper order', function() {
assert.equal(suggestions[0].querySelector('.js-name').textContent,
'John');
assert.equal(suggestions[1].querySelector('.js-name').textContent,
'Mary');
});
test('each match has no id attribute', function() {
suggestions.forEach(function(suggestion) {
assert.equal(suggestion.id, '');
});
});
test('each match has a ci--action-menu class', function() {
suggestions.forEach(function(suggestion) {
assert.isTrue(suggestion.classList.contains('ci--action-menu'));
});
});
test('each match highlights the matching part', function() {
suggestions.forEach(function(suggestion) {
assert.equal(suggestion.querySelector('.ci__mark').textContent,
'1111');
});
});
suite('high load', function() {
test('should show 50 suggestions', function() {
cloneMockContactResults(50);
subject.update('1234');
subject.showOverlay();
getSuggestions();
assert.equal(suggestions.length, 50);
});
test('should show 50 suggestions with 1 FB contact', function() {
cloneMockContactResults(49);
MockFbContacts.mResult = mockResultFb.slice(0, 1);
subject.update('1234');
subject.showOverlay();
getSuggestions();
assert.equal(suggestions.length, 50);
});
});
});
suite('hide overlay', function() {
setup(function() {
subject.hideOverlay();
});
test('should hide the overlay', function() {
assert.equal(subject.overlay.getAttribute('aria-hidden'), 'true');
assert.isFalse(subject.overlay.classList.contains('display'));
});
});
test('#show overlay of all numbers of contact', function() {
MockContacts.mResult = mockResult3;
subject.update('1234');
subject.showOverlay();
assert.equal(
subject.overlay.querySelector('#contact-list').childElementCount,
4, 'should have 3 items + cancel button into overlay list');
assert.isFalse(subject.overlay.hidden, 'should show suggestion list');
});
suite('#tap on suggestions list', function() {
setup(function() {
this.sinon.spy(MockCallHandler, 'call');
this.sinon.spy(subject, 'hideOverlay');
MockContacts.mResult = mockResult1;
subject.update('1234');
});
[0, 1].forEach(function(ci) {
test('with one SIM in slot ' + ci, function() {
SimSettingsHelper._defaultCards.outgoingCall = ci;
document.body.querySelector('.js-suggestion-item').click();
sinon.assert.calledWith(MockCallHandler.call, '1234567890', ci);
});
});
test('with two SIMs', function() {
MockNavigatorMozIccManager.addIcc(1, {});
document.body.querySelector('.js-suggestion-item').click();
sinon.assert.notCalled(MockCallHandler.call);
sinon.assert.calledOnce(subject.hideOverlay);
});
});
});
});

View file

@ -0,0 +1,390 @@
/* global ConfirmDialog, MockCall, MocksHelper, MockIccHelper,
MockNavigatorMozMobileConnections, MockNavigatorMozTelephony,
MockTelephonyMessages, TelephonyHelper, TelephonyMessages, Promise */
'use strict';
require('/shared/test/unit/mocks/mock_confirm_dialog.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js');
require('/shared/test/unit/mocks/mock_navigator_moz_telephony.js');
require('/shared/test/unit/mocks/dialer/mock_contacts.js');
require('/shared/test/unit/mocks/dialer/mock_telephony_messages.js');
require('/shared/test/unit/mocks/dialer/mock_call.js');
require('/dialer/test/unit/mock_icc_helper.js');
require('/dialer/js/telephony_helper.js');
var mocksHelperForTelephonyHelper = new MocksHelper([
'Call',
'Contacts',
'ConfirmDialog',
'LazyLoader',
'IccHelper',
'TelephonyMessages'
]).init();
function TelephonyCall() {}
suite('telephony helper', function() {
const DTMF_SEPARATOR_PAUSE_DURATION = 3000;
var subject;
var realMozTelephony;
var realMozMobileConnections;
var spyConfirmShow;
var mockTelephony;
var mockCall;
var mockPromise;
mocksHelperForTelephonyHelper.attachTestHelpers();
suiteSetup(function() {
MockCall.prototype = new TelephonyCall();
subject = TelephonyHelper;
realMozTelephony = navigator.mozTelephony;
navigator.mozTelephony = MockNavigatorMozTelephony;
realMozMobileConnections = navigator.mozMobileConnections;
navigator.mozMobileConnections = MockNavigatorMozMobileConnections;
});
suiteTeardown(function() {
navigator.mozTelephony = realMozTelephony;
navigator.mozMobileConnections = realMozMobileConnections;
});
setup(function() {
spyConfirmShow = this.sinon.spy(ConfirmDialog, 'show');
mockTelephony = this.sinon.mock(MockNavigatorMozTelephony);
mockCall = new MockCall('123456789', 'dialing', 0);
MockNavigatorMozMobileConnections[0].voice = {};
mockPromise = Promise.resolve(mockCall);
this.sinon.stub(MockNavigatorMozTelephony, 'dial',
function() { return mockPromise;});
this.sinon.stub(MockNavigatorMozTelephony, 'dialEmergency',
function() { return mockPromise;});
this.sinon.stub(MockNavigatorMozTelephony, 'sendTones', function() {
return Promise.resolve();
});
});
teardown(function() {
MockNavigatorMozMobileConnections.mTeardown();
MockNavigatorMozTelephony.mTeardown();
});
test('should sanitize the given phone number before dialing', function() {
var dialNumber = '(01) 45.34 55-20';
subject.call(dialNumber, 0);
sinon.assert.calledWith(navigator.mozTelephony.dial, '0145345520');
});
suite('Emergency dialing >', function() {
var initialState;
setup(function() {
initialState = MockIccHelper.mCardState;
MockIccHelper.mCardState = 'unknown';
MockNavigatorMozMobileConnections[0].voice = { emergencyCallsOnly: true };
});
teardown(function() {
MockIccHelper.mCardState = initialState;
document.dispatchEvent(new CustomEvent('visibilitychange'));
});
test('should display the connecting message', function() {
subject.call('112', 0);
sinon.assert.calledWith(spyConfirmShow, 'connectingEllipsis', '');
});
test('should hide the connecting message', function() {
subject.call('112', 0);
var spyConfirmHide = this.sinon.spy(ConfirmDialog, 'hide');
document.dispatchEvent(new CustomEvent('visibilitychange'));
sinon.assert.calledOnce(spyConfirmHide);
});
suite('when there is no sim card', function() {
test('it should always dial emergency with the first service',
function() {
var dialNumber = '112';
subject.call(dialNumber, 0);
sinon.assert.calledWith(navigator.mozTelephony.dialEmergency, '112');
});
});
suite('when there is a sim card', function() {
setup(function() {
MockNavigatorMozMobileConnections[0].iccId = 12;
});
test('it should dial emergency with the default service', function() {
var dialNumber = '112';
subject.call(dialNumber, 0);
sinon.assert.calledWith(navigator.mozTelephony.dialEmergency,
'112');
});
});
});
test('should dialEmergency if the connection is emergency only',
function() {
MockNavigatorMozMobileConnections[0].voice = { emergencyCallsOnly: true };
var dialNumber = '112';
subject.call(dialNumber, 0);
sinon.assert.calledWith(navigator.mozTelephony.dialEmergency, '112');
});
test('should NOT hold the active line before dialing',
function() {
var dialNumber = '123456';
var holdStub = this.sinon.stub();
var mockActive = {
number: '1111',
state: 'connected',
hold: holdStub
};
MockNavigatorMozTelephony.active = mockActive;
subject.call(dialNumber, 0);
delete MockNavigatorMozTelephony.active;
sinon.assert.calledWith(navigator.mozTelephony.dial, dialNumber);
assert.isFalse(holdStub.calledBefore(navigator.mozTelephony.dial));
});
test('should not hold the active group call before dialing (if there is one)',
function() {
var dialNumber = '123456';
var holdStub = this.sinon.stub();
MockNavigatorMozTelephony.conferenceGroup.calls =
[{number: '111111', serviceId: 0}, {number: '222222', serviceId: 0}];
MockNavigatorMozTelephony.conferenceGroup.state = 'connected';
MockNavigatorMozTelephony.conferenceGroup.hold = holdStub;
MockNavigatorMozTelephony.active =
MockNavigatorMozTelephony.conferenceGroup;
subject.call(dialNumber, 0);
delete MockNavigatorMozTelephony.active;
sinon.assert.calledWith(navigator.mozTelephony.dial, dialNumber);
sinon.assert.notCalled(holdStub);
});
test('should return null serviceId - no call', function() {
assert.equal(subject.getInUseSim(), null);
});
test('should return call serviceId - call', function() {
MockNavigatorMozTelephony.calls = [{number: '111111', serviceId: 0}];
assert.equal(subject.getInUseSim(), 0);
});
test('should return call serviceId - conference call', function() {
MockNavigatorMozTelephony.conferenceGroup.calls =
[{number: '222222', serviceId: 0}, {number: '333333', serviceId: 0}];
assert.equal(subject.getInUseSim(), 0);
});
test('should display an error if there is no network', function() {
MockNavigatorMozMobileConnections[0].voice = null;
var dialNumber = '01 45 34 55 20';
this.sinon.stub(MockTelephonyMessages, 'displayMessage');
subject.call(dialNumber, 0);
sinon.assert.calledWith(MockTelephonyMessages.displayMessage,
'NoNetwork');
});
test('should display an error if the number is invalid', function() {
var dialNumber = '01sfsafs45 34 55 20';
this.sinon.stub(MockTelephonyMessages, 'displayMessage');
subject.call(dialNumber, 0);
sinon.assert.calledWith(MockTelephonyMessages.displayMessage,
'BadNumber');
});
suite('Callbacks binding', function() {
test('should trigger oncall as soon as we get a call object',
function(done) {
subject.call('123', 0, function() {
done();
});
});
test('should trigger the onconnected listeners when connected',
function(done) {
var onconnectedHandler = function() {
done();
};
subject.call('123', 0, null, onconnectedHandler);
mockPromise.then(function() {
mockCall.triggerEvent('connected');
});
});
test('should bind the ondisconnected callback', function(done) {
var ondisconnected = this.sinon.stub();
subject.call('123', 0, null, null, ondisconnected);
mockPromise.then(function() {
mockCall.triggerEvent('disconnected');
sinon.assert.calledOnce(ondisconnected);
}).then(done, done);
});
});
suite('Call error handling', function() {
test('should display the BadNumber message', function(done) {
mockPromise = Promise.reject('BadNumberError');
this.sinon.stub(MockTelephonyMessages, 'handleError',
function(errorName, number, messageType) {
assert.equal(errorName, 'BadNumberError');
assert.equal(number, '123');
assert.equal(messageType, MockTelephonyMessages.REGULAR_CALL);
done();
});
subject.call('123', 0);
});
test('should handle RadioNotAvailable', function(done) {
mockPromise = Promise.reject('RadioNotAvailable');
this.sinon.stub(MockTelephonyMessages, 'handleError',
function(errorName, number, messageType) {
assert.equal(errorName, 'RadioNotAvailable');
assert.equal(number, '123');
assert.equal(messageType, MockTelephonyMessages.REGULAR_CALL);
done();
});
subject.call('123', 0);
});
test('should handle OtherConnectionInUse', function(done) {
mockPromise = Promise.reject('OtherConnectionInUse');
this.sinon.stub(MockTelephonyMessages, 'handleError',
function(errorName, number, messageType) {
assert.equal(errorName, 'OtherConnectionInUse');
assert.equal(number, '123');
assert.equal(messageType, MockTelephonyMessages.REGULAR_CALL);
done();
});
subject.call('123', 0);
});
test('should handle unknown errors', function(done) {
mockPromise = Promise.reject('Gloubiboulga');
this.sinon.stub(MockTelephonyMessages, 'handleError',
function(errorName, number, messageType) {
assert.equal(errorName, 'Gloubiboulga');
assert.equal(number, '123');
assert.equal(messageType, MockTelephonyMessages.REGULAR_CALL);
done();
});
subject.call('123', 0, null, null, null);
});
});
test('should display a message if we didn\'t get a call back',
function(done) {
mockPromise = Promise.reject('');
this.sinon.stub(MockTelephonyMessages, 'handleError',
function(errorName, number, messageType) {
assert.equal(errorName, '');
assert.equal(number, '123');
assert.equal(messageType, MockTelephonyMessages.REGULAR_CALL);
done();
});
subject.call('123', 0);
});
test('should dial with correct card index', function() {
MockNavigatorMozMobileConnections.mAddMobileConnection();
MockNavigatorMozMobileConnections[1].voice = {};
subject.call('123', 1);
sinon.assert.calledWith(navigator.mozTelephony.dial, '123', 1);
});
suite('<<pause>> DTMF separator', function() {
test('should allow dialing number with DTMF separator', function() {
subject.call('1233241,,123', 0);
sinon.assert.calledWith(navigator.mozTelephony.dial, '1233241');
});
test('a number that starts with pause is invalid', function() {
this.sinon.spy(TelephonyMessages, 'displayMessage');
subject.call(',012023423', 0);
sinon.assert.calledWith(TelephonyMessages.displayMessage, 'BadNumber');
});
test('should send DTMF tones after connection', function(done) {
subject.call('1233241,123', 0);
mockPromise.then(function() {
sinon.assert.notCalled(MockNavigatorMozTelephony.sendTones);
// Notify the connected event to the TelephonyCall.
mockCall.triggerEvent('connected');
}).then(function() {
// Start playing the first tone group and pause.
sinon.assert.calledWith(MockNavigatorMozTelephony.sendTones, '123',
DTMF_SEPARATOR_PAUSE_DURATION, null, 0);
done();
});
});
test('should send DTMF tones with correct card index', function(done) {
subject.call('123456,123', 1);
mockPromise.then(function() {
sinon.assert.notCalled(MockNavigatorMozTelephony.sendTones);
// Notify the connected event to the TelephonyCall.
mockCall.triggerEvent('connected');
}).then(function() {
// Start playing the first tone group and pause.
sinon.assert.calledWith(MockNavigatorMozTelephony.sendTones, '123',
DTMF_SEPARATOR_PAUSE_DURATION, null, 1);
done();
});
});
test('should wait 3 seconds after each separator', function(done) {
subject.call('123456789,123,,456,,,789', 0);
mockPromise.then(function() {
sinon.assert.notCalled(MockNavigatorMozTelephony.sendTones);
// Notify the connected event to the TelephonyCall.
mockCall.triggerEvent('connected');
}).then(function() {
// Start playing the first tone group and pause.
sinon.assert.calledWith(
MockNavigatorMozTelephony.sendTones, '123',
DTMF_SEPARATOR_PAUSE_DURATION, null, 0);
return Promise.resolve();
}).then(function() {
// Start playing the second tone group and pauses.
sinon.assert.calledWith(
MockNavigatorMozTelephony.sendTones, '456',
DTMF_SEPARATOR_PAUSE_DURATION * 2, null, 0);
return Promise.resolve();
}).then(function() {
// Start playing the third tone group and pauses.
sinon.assert.calledWith(
MockNavigatorMozTelephony.sendTones, '789',
DTMF_SEPARATOR_PAUSE_DURATION * 3, null, 0);
done();
});
});
test('should not play the pauses (",") at the end of the number',
function(done) {
subject.call('123456789,,,', 0);
mockPromise.then(function() {
sinon.assert.notCalled(MockNavigatorMozTelephony.sendTones);
// Notify the connected event to the TelephonyCall.
mockCall.triggerEvent('connected');
}).then(function() {
sinon.assert.notCalled(MockNavigatorMozTelephony.sendTones);
done();
});
});
});
});

View file

@ -0,0 +1,236 @@
'use strict';
/* global MocksHelper, MockAudioContext, MockNavigatorMozTelephony, TonePlayer
*/
require('/shared/js/dialer/tone_player.js');
require('/shared/test/unit/mocks/mock_audio.js');
require('/shared/test/unit/mocks/mock_navigator_moz_telephony.js');
var mocksForTonePlayer = new MocksHelper([
'AudioContext'
]).init();
suite('shared/dialer/TonePlayer', function() {
var realMozTelephony;
var isDocumentHidden = false;
mocksForTonePlayer.attachTestHelpers();
suiteSetup(function() {
realMozTelephony = navigator.mozTelephony;
navigator.mozTelephony = MockNavigatorMozTelephony;
Object.defineProperty(document, 'hidden', {
configurable: true,
get: function() {
return isDocumentHidden;
}
});
});
suiteTeardown(function() {
navigator.mozTelephony = realMozTelephony;
});
teardown(function() {
TonePlayer.teardown();
MockNavigatorMozTelephony.mTeardown();
isDocumentHidden = false;
});
test('should do nothing before init', function() {
TonePlayer._ensureAudio();
assert.equal(MockAudioContext.instances.length, 0);
});
suite('init', function() {
setup(function() {
TonePlayer.init('telephony');
});
test('should not instantiate an audio context', function() {
assert.equal(MockAudioContext.instances.length, 0);
});
test('should mark the object as initialized', function() {
assert.isTrue(TonePlayer._initialized);
});
});
suite('visibilitychange event', function() {
test('should trash the audio context when hidden', function() {
TonePlayer.init('content');
this.sinon.spy(TonePlayer, '_trashAudio');
isDocumentHidden = true;
window.dispatchEvent(new CustomEvent('visibilitychange'));
sinon.assert.calledOnce(TonePlayer._trashAudio);
});
suite('during a call', function() {
setup(function() {
MockNavigatorMozTelephony.calls = [{}];
});
test('should trash the audio context using a non-telephony channel',
function() {
TonePlayer.init('content');
this.sinon.spy(TonePlayer, '_trashAudio');
isDocumentHidden = true;
window.dispatchEvent(new CustomEvent('visibilitychange'));
sinon.assert.calledOnce(TonePlayer._trashAudio);
});
test('should not trash the audio context using a telephony channel',
function() {
TonePlayer.init('telephony');
this.sinon.spy(TonePlayer, '_trashAudio');
isDocumentHidden = true;
window.dispatchEvent(new CustomEvent('visibilitychange'));
sinon.assert.notCalled(TonePlayer._trashAudio);
});
});
suite('during a conference call', function() {
setup(function() {
MockNavigatorMozTelephony.calls = [];
MockNavigatorMozTelephony.conferenceGroup.calls = [{}];
});
test('should trash the audio context using a non-telephony channel',
function() {
TonePlayer.init('content');
this.sinon.spy(TonePlayer, '_trashAudio');
isDocumentHidden = true;
window.dispatchEvent(new CustomEvent('visibilitychange'));
sinon.assert.calledOnce(TonePlayer._trashAudio);
});
test('should not trash the audio context using a telephony channel',
function() {
TonePlayer.init('telephony');
this.sinon.spy(TonePlayer, '_trashAudio');
isDocumentHidden = true;
window.dispatchEvent(new CustomEvent('visibilitychange'));
sinon.assert.notCalled(TonePlayer._trashAudio);
});
});
});
suite('oncallschanged event', function() {
setup(function() {
isDocumentHidden = true;
MockNavigatorMozTelephony.calls = [{}];
});
suite('using a telephony channel when hidden', function() {
setup(function() {
TonePlayer.init('telephony');
});
test('should only trash the audio context when a call ends',
function() {
this.sinon.spy(TonePlayer, '_trashAudio');
MockNavigatorMozTelephony.mTriggerCallsChanged();
sinon.assert.notCalled(TonePlayer._trashAudio);
MockNavigatorMozTelephony.calls = [];
MockNavigatorMozTelephony.mTriggerCallsChanged();
sinon.assert.calledOnce(TonePlayer._trashAudio);
});
test('should not trash the audio context during a conference call',
function() {
this.sinon.spy(TonePlayer, '_trashAudio');
MockNavigatorMozTelephony.mTriggerCallsChanged();
sinon.assert.notCalled(TonePlayer._trashAudio);
MockNavigatorMozTelephony.calls = [];
MockNavigatorMozTelephony.conferenceGroup.calls = [{}];
MockNavigatorMozTelephony.mTriggerCallsChanged();
sinon.assert.notCalled(TonePlayer._trashAudio);
});
test('should trash the audio context when the conference call ends',
function() {
this.sinon.spy(TonePlayer, '_trashAudio');
MockNavigatorMozTelephony.calls = [];
MockNavigatorMozTelephony.conferenceGroup.calls = [{}];
MockNavigatorMozTelephony.mTriggerCallsChanged();
sinon.assert.notCalled(TonePlayer._trashAudio);
MockNavigatorMozTelephony.conferenceGroup.calls = [];
MockNavigatorMozTelephony.mTriggerCallsChanged();
sinon.assert.calledOnce(TonePlayer._trashAudio);
});
});
suite('using another channel when hidden', function() {
setup(function() {
TonePlayer.init('content');
});
test('should always trash the audio context', function() {
this.sinon.spy(TonePlayer, '_trashAudio');
MockNavigatorMozTelephony.mTriggerCallsChanged();
sinon.assert.calledOnce(TonePlayer._trashAudio);
MockNavigatorMozTelephony.conferenceGroup.calls = [{}];
MockNavigatorMozTelephony.mTriggerCallsChanged();
sinon.assert.calledTwice(TonePlayer._trashAudio);
});
});
});
suite('playSequence', function() {
setup(function() {
TonePlayer.init('content');
});
test('should instantiate a new audio context if needed', function() {
assert.equal(MockAudioContext.instances.length, 0);
TonePlayer.playSequence({'1': ['697', 1209]});
assert.equal(MockAudioContext.instances.length, 1);
var ctx = MockAudioContext.instances[0];
assert.equal(ctx.mozAudioChannelType, 'content');
});
});
suite('start', function() {
setup(function() {
TonePlayer.init('content');
});
test('should instantiate a new audio context if needed', function() {
assert.equal(MockAudioContext.instances.length, 0);
TonePlayer.start(['697', 1209], true);
assert.equal(MockAudioContext.instances.length, 1);
var ctx = MockAudioContext.instances[0];
assert.equal(ctx.mozAudioChannelType, 'content');
});
});
suite('_trashAudio', function() {
setup(function() {
TonePlayer.init('telephony');
TonePlayer._ensureAudio();
});
test('telephony channel released', function() {
TonePlayer._trashAudio();
assert.isNull(TonePlayer._audioContext);
});
test('should remember the channel for the next _ensureAudio()', function() {
TonePlayer._trashAudio();
TonePlayer._ensureAudio();
var ctx = MockAudioContext.instances[1];
assert.equal(ctx.mozAudioChannelType, 'telephony');
});
});
});

View file

@ -0,0 +1,283 @@
/* global MockMozVoicemail, MockNavigatorSettings, Voicemail */
'use strict';
require('/shared/js/dialer/voicemail.js');
require('/dialer/test/unit/mock_mozVoicemail.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
suite('dialer/voicemail', function() {
var realMozVoicemail;
var realMozSettings;
suiteSetup(function() {
realMozVoicemail = navigator.mozVoicemail;
navigator.mozVoicemail = MockMozVoicemail;
realMozSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
});
suiteTeardown(function() {
navigator.mozVoicemail = realMozVoicemail;
navigator.mozSettings = realMozSettings;
});
var VOICEMAIL_NUMBER_SIM_1 = '123';
var VOICEMAIL_NUMBER_SIM_2 = '777';
var NON_VOICEMAIL_NUMBER = '1234567890';
// We have two sources of voicemail number: mozVoicemailNumber and
// 'ril.iccInfo.mbdn' in mozSettings. Since we support dual SIM now,
// each of them could have 0, 1, or 2 voicemail numbers. All the
// combinations of voicemail number sources are covered in array 'scenarios'.
var scenarios = [
{
name: 'mozVoicemail has voicemail number of SIM 1',
mozVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1],
mozSettingsVoicemailNumber: null,
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isFalse(isVoicemailNumber);
}
},
{
name: 'mozVoicemail has voicemail number of SIM 2',
mozVoicemailNumber: [undefined, VOICEMAIL_NUMBER_SIM_2],
mozSettingsVoicemailNumber: null,
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isFalse(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozSettings has voicemail number of SIM 1',
mozVoicemailNumber: null,
mozSettingsVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isFalse(isVoicemailNumber);
}
},
{
name: 'mozSettings has voicemail number of SIM 2',
mozVoicemailNumber: null,
mozSettingsVoicemailNumber: [undefined, VOICEMAIL_NUMBER_SIM_2],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isFalse(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozVoicemail has voicemail number of SIM 1 and SIM 2',
mozVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1, VOICEMAIL_NUMBER_SIM_2],
mozSettingsVoicemailNumber: null,
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozVoicemail and mozSettings have voicemail number of SIM 1',
mozVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1],
mozSettingsVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isFalse(isVoicemailNumber);
}
},
{
name: 'mozVoicemail has voicemail number of SIM 1 and ' +
'mozSettings has voicemail number of SIM 2',
mozVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1],
mozSettingsVoicemailNumber: [undefined, VOICEMAIL_NUMBER_SIM_2],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozVoicemail has voicemail number of SIM 2 and ' +
'mozSettings has voicemail number of SIM 1',
mozVoicemailNumber: [undefined, VOICEMAIL_NUMBER_SIM_2],
mozSettingsVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozVoicemail and mozSettings have voicemail number of SIM card 2',
mozVoicemailNumber: [undefined, VOICEMAIL_NUMBER_SIM_2],
mozSettingsVoicemailNumber: [undefined, VOICEMAIL_NUMBER_SIM_2],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isFalse(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozSettings has both voicemail number of SIM 1 and SIM 2',
mozVoicemailNumber: null,
mozSettingsVoicemailNumber: [
VOICEMAIL_NUMBER_SIM_1, VOICEMAIL_NUMBER_SIM_2
],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozVoicemail has voicemail number of SIM 1 and SIM 2 and ' +
'mozSettings has voicemail number of SIM 1',
mozVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1, VOICEMAIL_NUMBER_SIM_2],
mozSettingsVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozVoicemail has voicemail number of SIM 1 and SIM 2 and ' +
'mozSettings has voicemail number of SIM 2',
mozVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1, VOICEMAIL_NUMBER_SIM_2],
mozSettingsVoicemailNumber: [undefined, VOICEMAIL_NUMBER_SIM_2],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozVoicemail has voicemail number of SIM 1 and ' +
'mozSettings has voicemail number of SIM 1 and SIM 2',
mozVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1],
mozSettingsVoicemailNumber: [
VOICEMAIL_NUMBER_SIM_1, VOICEMAIL_NUMBER_SIM_2
],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozVoicemail has voicemail number of SIM 2 and ' +
'mozSettings has voicemail number of SIM 1 and SIM 2',
mozVoicemailNumber: [undefined, VOICEMAIL_NUMBER_SIM_2],
mozSettingsVoicemailNumber: [
VOICEMAIL_NUMBER_SIM_1, VOICEMAIL_NUMBER_SIM_2
],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
},
{
name: 'mozVoicemail and mozSettings have voicemail number of ' +
'SIM 1 and SIM 2',
mozVoicemailNumber: [VOICEMAIL_NUMBER_SIM_1, VOICEMAIL_NUMBER_SIM_2],
mozSettingsVoicemailNumber: [
VOICEMAIL_NUMBER_SIM_1, VOICEMAIL_NUMBER_SIM_2
],
assertionWhenCallingVoicemailNumber: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
},
assertionWhenCallingVoicemailNumber2: function(isVoicemailNumber) {
assert.isTrue(isVoicemailNumber);
}
}
];
scenarios.forEach(function(scenario) {
suite(scenario.name, function() {
setup(function() {
this.sinon.stub(
navigator.mozVoicemail, 'getNumber', function(cardId) {
return scenario.mozVoicemailNumber &&
scenario.mozVoicemailNumber[cardId];
});
MockNavigatorSettings.createLock().set({
'ril.iccInfo.mbdn': scenario.mozSettingsVoicemailNumber
});
});
test('call the voicemail number via SIM 1', function(done) {
var serviceId = 0;
Voicemail.check(VOICEMAIL_NUMBER_SIM_1, serviceId).then(
function(isVoicemailNumber) {
scenario.assertionWhenCallingVoicemailNumber(isVoicemailNumber);
}, function(reason) {
assert.fail('should not reject promise: ' + reason);
}).then(done, done);
});
test('call the voicemail number via SIM 2', function(done) {
var serviceId = 1; // serviceId 1 means SIM 2
Voicemail.check(VOICEMAIL_NUMBER_SIM_2, serviceId).then(
function(isVoicemailNumber) {
scenario.assertionWhenCallingVoicemailNumber2(isVoicemailNumber);
}, function(reason) {
assert.fail('should not reject promise: ' + reason);
}).then(done, done);
});
test('call a number is not the voicemail number', function(done) {
var serviceId = 0;
Voicemail.check(NON_VOICEMAIL_NUMBER, serviceId).then(
function(isVoicemailNumber) {
assert.isFalse(isVoicemailNumber);
}, function(reason) {
assert.fail('should not reject promise: ' + reason);
}).then(done, done);
});
test('call a number that is not the voicemail number of SIM 2 ' +
'via SIM 2', function(done) {
var serviceId = 1;
Voicemail.check(NON_VOICEMAIL_NUMBER, serviceId).then(
function(isVoicemailNumber) {
assert.isFalse(isVoicemailNumber);
}, function(reason) {
assert.fail('should not reject promise: ' + reason);
}).then(done, done);
});
});
});
test('Blank number is not voicemail', function(done) {
this.sinon.stub(navigator.mozVoicemail, 'getNumber').returns('');
MockNavigatorSettings.createLock().set({'ril.iccInfo.mbdn': ''});
Voicemail.check('', 0).then(
function(isVoicemailNumber) {
assert.isFalse(isVoicemailNumber);
}, function(reason) {
assert.fail('should not reject promise: ' + reason);
}).then(done, done);
});
});