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,202 @@
/* global LazyLoader, DetailsController,
DetailsUI, mozContact, ContactsService */
'use strict';
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/js/lazy_loader.js');
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/views/details/js/boot.js');
suite('Details Boot', function() {
var realDetailsController, realDetailsUI;
setup(function() {
loadBodyHTML('/contacts/views/details/details.html');
realDetailsController = window.DetailsController;
realDetailsUI = window.DetailsUI;
window.DetailsController = {
init: function foo() {},
setActivity: function boo() {}
};
window.DetailsUI = {
init: function foo() {},
render: function boo() {}
};
});
teardown(function() {
document.body.innerHTML = '';
window.DetailsController = realDetailsController;
window.DetailsUI = realDetailsUI;
realDetailsController = null;
realDetailsUI = null;
});
suite('DOMContentLoaded', function() {
var stub;
setup(function() {
stub = this.sinon.stub(LazyLoader, 'load', function(files) {
return {
then: function(cb) {
cb();
}
};
});
window.dispatchEvent(new CustomEvent('DOMContentLoaded'));
});
teardown(function() {
LazyLoader.load.restore();
});
test(' > LazyLoader must be called 2 times', function() {
// We must have 2 calls to LazyLoader
assert.isTrue(stub.calledTwice);
});
test(' > First call must ensure localization', function() {
// First of all we need to bring l10n library for localization
var firstCall = stub.getCall(0);
assert.isTrue(Array.isArray(firstCall.args[0]));
assert.equal(firstCall.args[0][0], '/shared/js/l10n.js');
});
test(' > Second call must load the template', function() {
// Secondly we lazy load the panel, so the param must be the element
// of the DOM to fill with the template
var secondCall = stub.getCall(1);
assert.equal(secondCall.args[0][0].id, 'view-contact-details');
});
});
suite('Loaded', function() {
var realMozSetMessage;
suiteSetup(function() {
realMozSetMessage = navigator.mozSetMessageHandler;
navigator.mozSetMessageHandler = function foo() {};
});
suiteTeardown(function() {
navigator.mozSetMessageHandler = realMozSetMessage;
realMozSetMessage = null;
});
setup(function() {
this.sinon.stub(LazyLoader, 'load', function(files) {
return {
then: function(cb) {
cb();
}
};
});
});
test(' > Controller is initialized', function() {
var DetailsControllerSpy = this.sinon.spy(DetailsController, 'init');
window.dispatchEvent(new CustomEvent('load'));
assert.isTrue(DetailsControllerSpy.called );
});
test(' > UI is initialized', function() {
var DetailsUISpy = this.sinon.spy(DetailsUI, 'init');
window.dispatchEvent(new CustomEvent('load'));
assert.isTrue(DetailsUISpy.calledOnce);
// Make sure the confirmation message dialog existed
assert.isNotNull(document.getElementById('confirmation-message'));
});
});
suite('Activity', function() {
var realContactsService, testContact;
var fakeActivity = {
source: {
data: {
params: {
'id': 'testID'
}
}
}
};
suiteSetup(function() {
realContactsService = window.ContactsService;
testContact = new mozContact();
testContact.givenName = ['test'];
window.ContactsService = {
get: function get(id, callback) {
callback(testContact);
},
getCount: function getCount(callback) {
callback(1);
}
};
});
suiteTeardown(function() {
window.ContactsService = realContactsService;
realContactsService = null;
});
setup(function() {
this.sinon.stub(LazyLoader, 'load', function(files) {
return {
then: function(cb) {
cb();
}
};
});
});
test(' > Listener to activity is set properly', function(done) {
this.sinon.stub(navigator, 'mozSetMessageHandler', function() {
done();
});
window.dispatchEvent(new CustomEvent('load'));
});
test(' > UI is updated properly with activity params', function(done) {
var DetailsUIRenderSpy = this.sinon.spy(DetailsUI, 'render');
var ContactsServiceGetSpy = this.sinon.spy(ContactsService, 'get');
var ContactsServiceGetCountSpy =
this.sinon.spy(ContactsService, 'getCount');
this.sinon.stub(navigator, 'mozSetMessageHandler',
function(name, callback) {
callback(fakeActivity);
assert.isTrue(ContactsServiceGetSpy.called);
assert.isTrue(ContactsServiceGetCountSpy.called);
assert.equal(
ContactsServiceGetSpy.getCall(0).args[0],
fakeActivity.source.data.params.id
);
assert.isTrue(DetailsUIRenderSpy.called, 'render called');
assert.equal(
DetailsUIRenderSpy.getCall(0).args[0],
testContact
);
done();
}
);
window.dispatchEvent(new CustomEvent('load'));
});
test(' > Activity is cached in Controller', function(done) {
var DetailsActivitySpy = this.sinon.spy(DetailsController, 'setActivity');
this.sinon.stub(navigator, 'mozSetMessageHandler',
function(name, callback) {
callback(fakeActivity);
assert.isTrue(DetailsActivitySpy.called);
done();
}
);
window.dispatchEvent(new CustomEvent('load'));
});
});
});

View file

@ -0,0 +1,135 @@
/* global LazyLoader, DetailsController, ContactsService, MockL10n */
/* global MockContactAllFields, MatchService */
'use strict';
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/js/lazy_loader.js');
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/js/match_service.js');
require('/shared/js/text_normalizer.js');
require('/shared/js/simple_phone_matcher.js');
require('/shared/js/contacts/contacts_matcher.js');
require('/shared/js/contacts/import/utilities/misc.js');
require('/shared/test/unit/mocks/mock_contact_all_fields.js');
requireApp('communications/contacts/views/details/js/details_controller.js');
suite('DetailsController', function() {
var realMozL10n;
var mozContact;
var stubLazyLoader;
var stubContactServiceSave;
var realContactToVcardBlob;
suiteSetup(function() {
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
DetailsController.init();
realContactToVcardBlob = window.ContactToVcardBlob;
window.ContactToVcardBlob = function foo() {};
});
suiteTeardown(function() {
navigator.mozL10n = realMozL10n;
window.ContactToVcardBlob = realContactToVcardBlob;
realMozL10n = null;
realContactToVcardBlob = null;
});
setup(function() {
mozContact = new MockContactAllFields();
stubContactServiceSave = this.sinon.stub(ContactsService, 'save',
function(contact, callback) {});
stubLazyLoader = this.sinon.stub(LazyLoader, 'load',
function(files, callback) {
callback();
});
});
teardown(function() {
stubLazyLoader = null;
stubLazyLoader = null;
stubContactServiceSave = null;
mozContact = null;
});
suite('Close button', function() {
test(' > must call handleBackAction when an event is received',
function(done) {
var spy = this.sinon.spy(window.history, 'back');
window.addEventListener('backAction', function() {
assert.isTrue(spy.calledOnce);
done();
});
window.dispatchEvent(new CustomEvent('backAction'));
});
});
suite('Edit button', function() {
test('> must call handleEditAction when an event is received', function() {
//TODO: add tests when edit mode works
assert.isTrue(true);
});
});
suite('Share', function() {
var spyContactToVcardBlob;
setup(function() {
spyContactToVcardBlob = this.sinon.spy(window, 'ContactToVcardBlob');
});
teardown(function() {
spyContactToVcardBlob = null;
});
test(' > Must handle shareAction event', function(done) {
window.addEventListener('shareAction', function(evt) {
assert.equal(evt.detail.contact, mozContact);
assert.isTrue(spyContactToVcardBlob.calledOnce);
done();
});
window.dispatchEvent(new CustomEvent('shareAction', {'detail': {
'contact': mozContact
}}));
});
});
suite('Find Duplicates', function() {
test(' > Must handle find duplicates event', function(done) {
var stubMatch = this.sinon.stub(MatchService, 'match');
window.addEventListener('findDuplicatesAction', function() {
assert.isTrue(stubMatch.calledWith(mozContact.id));
done();
});
window.dispatchEvent(new CustomEvent('findDuplicatesAction', {'detail':
{'contactId': mozContact.id}}));
});
});
suite('Toggle favorite', function() {
test(' > Must handle toggle favorite event', function(done) {
window.addEventListener('toggleFavoriteAction', function() {
assert.isTrue(stubContactServiceSave.calledOnce);
done();
});
function isFavorite()
{
return mozContact != null && mozContact.category != null &&
mozContact.category.indexOf('favorite') != -1;
}
window.dispatchEvent(new CustomEvent('toggleFavoriteAction', {'detail':
{'contact': mozContact, 'isFavorite': isFavorite()}}));
});
});
});

View file

@ -0,0 +1,326 @@
/* global LazyLoader, DetailsUI, MockL10n, MockWebrtcClient */
/* global MockContactAllFields, ContactPhotoHelper, utils */
'use strict';
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/js/lazy_loader.js');
require('/shared/elements/gaia_subheader/script.js');
require('/shared/elements/gaia-header/dist/gaia-header.js');
require('/shared/js/contacts/contacts_buttons.js');
require('/shared/js/text_normalizer.js');
require('/shared/js/contacts/utilities/dom.js');
require('/shared/js/contacts/import/utilities/misc.js');
require('/shared/js/contacts/utilities/templates.js');
require('/shared/js/contact_photo_helper.js');
requireApp('communications/contacts/js/activities.js');
require('/shared/test/unit/mocks/mock_contact_all_fields.js');
requireApp(
'communications/contacts/test/unit/webrtc-client/mock_webrtc_client.js');
requireApp('communications/contacts/views/details/js/details_ui.js');
suite('DetailsUI', function() {
var realMozL10n;
var realWebrctClient;
var mozContact = null;
suiteSetup(function(done) {
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
realWebrctClient = window.WebrtcClient;
window.WebrtcClient = MockWebrtcClient;
// Load HTML
loadBodyHTML('/contacts/views/details/details.html');
// Add hook to template to "head"
var importHook = document.createElement('link');
importHook.setAttribute('rel', 'import');
importHook.setAttribute('href', '/contacts/elements/details.html');
document.head.appendChild(importHook);
// Fill the HTML
LazyLoader.load([document.getElementById('view-contact-details')],
function() {
mozContact = new MockContactAllFields();
DetailsUI.init();
DetailsUI.render(mozContact, 1, true);
done();
});
});
suiteTeardown(function() {
document.body.innerHTML = '';
navigator.mozL10n = realMozL10n;
realMozL10n = null;
window.WebrtcClient = realWebrctClient;
realWebrctClient = null;
mozContact = null;
});
setup(function() {
});
teardown(function() {
});
suite('Close button', function() {
test(' > must dispatch an event when clicked', function(done) {
window.addEventListener('backAction', function() {
done();
});
var clickEvent = new CustomEvent('action');
document.querySelector('#details-view-header').dispatchEvent(clickEvent);
});
});
suite('Favorite', function() {
test(' > header must be rendered with the favorite icon', function() {
var header = document.querySelector('#details-view-header');
assert.isTrue(header.classList.contains('favorite'));
});
test(' > toggle favorite button must display Remove string', function() {
var toggleFavoriteBtn = document.querySelector('#toggle-favorite');
assert.equal(toggleFavoriteBtn.getAttribute('data-l10n-id'),
'removeFavorite');
});
test(' > click toggle favorite button must dispatch an event',
function(done) {
window.addEventListener('toggleFavoriteAction', function(evt) {
assert.equal(evt.detail.contact, mozContact);
assert.isTrue(evt.detail.isFavorite);
done();
});
var toggleFavoriteBtn = document.querySelector('#toggle-favorite');
toggleFavoriteBtn.click();
});
test(' > favorite state must change when an event is received',
function(done) {
var nonFavoriteContact = new MockContactAllFields();
nonFavoriteContact.category = [];
window.addEventListener('toggleFavoriteDone', function(evt) {
assert.equal(evt.detail.contact, nonFavoriteContact);
var toggleFavoriteBtn = document.querySelector('#toggle-favorite');
assert.equal(toggleFavoriteBtn.getAttribute('data-l10n-id'),
'addFavorite');
var header = document.querySelector('#details-view-header');
assert.isFalse(header.classList.contains('favorite'));
done();
});
window.dispatchEvent(new CustomEvent('toggleFavoriteDone', {'detail':
{'contact': nonFavoriteContact}}));
});
});
suite('Org', function() {
var orgTitle;
setup(function() {
orgTitle = document.querySelector('#org-title');
});
teardown(function() {
orgTitle = null;
});
test(' > Contact has Org -> must be display properly', function() {
assert.equal(orgTitle.textContent, mozContact.org[0]);
assert.isFalse(orgTitle.classList.contains('hide'));
});
test(' > Contact hasnt Org -> must not be displayed', function() {
var nonOrgContact = new MockContactAllFields();
nonOrgContact.org = [];
DetailsUI.renderOrg(nonOrgContact);
assert.equal(orgTitle.textContent, '');
assert.isTrue(orgTitle.classList.contains('hide'));
});
});
suite('Phones', function() {
test(' > Phones must be rendered properly', function() {
var phones = document.querySelectorAll('div[data-tel]');
assert.equal(phones.length, mozContact.tel.length);
Array.prototype.forEach.call(phones, (phone, index) => {
assert.equal(phone.dataset.tel, mozContact.tel[index].value);
});
});
});
suite('Emails', function() {
test(' > Emails must be rendered properly', function() {
var emails =
document.querySelectorAll('li[data-mail]:not([data-template])');
assert.equal(emails.length, mozContact.email.length);
Array.prototype.forEach.call(emails, (listItem, index) => {
var h2 = listItem.querySelector('h2');
var button = listItem.querySelector('button[data-email]');
assert.equal(h2.getAttribute('data-l10n-id'),
mozContact.email[index].type[0]);
assert.equal(button.dataset.email, mozContact.email[index].value);
});
});
});
suite('WebrtcClient', function() {
test(' > start method must be called', function() {
var spy = this.sinon.spy(window.WebrtcClient, 'start');
var stub = this.sinon.stub(LazyLoader, 'load', function(files) {
return {
then: function(cb) {
cb();
}
};
});
// Force renderWebrtcClient
DetailsUI.renderWebrtcClient();
assert.isTrue(spy.called);
var call = stub.getCall(0);
assert.isTrue(Array.isArray(call.args[0]));
assert.equal(call.args[0][0],
'/contacts/style/webrtc-client/webrtc_client.css');
assert.equal(call.args[0][1],
'/contacts/js/webrtc-client/webrtc_client.js');
});
});
suite('Addresses', function() {
test(' > Addresses must be rendered properly', function() {
var addresses =
document.querySelectorAll('li[data-address]:not([data-template])');
assert.equal(addresses.length, mozContact.adr.length);
Array.prototype.forEach.call(addresses, (listItem, index) => {
var h2 = listItem.querySelector('h2');
var a = listItem.querySelector('a');
var address = a.textContent;
assert.equal(h2.getAttribute('data-l10n-id'),
mozContact.adr[index].type[0]);
// Assert all data is displayed properly
['Germany', 'Chemnitz',
'Chemnitz', '09034',
'Gotthardstrasse 22'].forEach(needle => {
assert.isTrue(address.includes(needle));
});
});
});
});
suite('Dates', function() {
test(' > Dates must be rendered properly', function() {
var dates =
document.querySelectorAll('li[aria-label="Date"]:not([data-template])');
assert.equal(dates.length, 1);
var h2 = dates[0].querySelector('h2');
var strong = dates[0].querySelector('strong');
var formatDate = utils.misc.formatDate(mozContact.bday);
assert.equal(h2.getAttribute('data-l10n-id'), 'birthday');
assert.equal(strong.textContent, formatDate);
});
});
suite('Notes', function() {
test(' > Notes must be rendered properly', function() {
var notes =
document.querySelectorAll('p[data-comment]:not([data-template])');
assert.equal(notes.length, mozContact.note.length);
Array.prototype.forEach.call(notes, (item, index) => {
assert.equal(item.textContent, mozContact.note[index]);
});
});
});
suite('Share button', function() {
var selector;
setup(function() {
selector = 'li[data-social]:not([data-template])';
});
teardown(function() {
selector = null;
});
test(' > Share button must be rendered properly', function() {
var shareBtn = document.querySelector(selector + ' #share_button');
assert.isFalse(shareBtn.classList.contains('hide'));
});
test(' > Share button must dispatch an event', function(done) {
window.addEventListener('shareAction', function() {
done();
});
var shareBtn = document.querySelector(selector + ' #share_button');
shareBtn.click();
});
});
suite('Find duplicates', function() {
var selector;
var realIsFavoriteChange;
setup(function() {
selector = 'li.duplicate-actions:not([data-template])';
realIsFavoriteChange = DetailsUI.isAFavoriteChange;
DetailsUI.isAFavoriteChange = false;
});
teardown(function() {
selector = null;
DetailsUI.isAFavoriteChange = realIsFavoriteChange;
});
test(' > Find duplicates buttons must be disabled', function() {
var findMergeButton =
document.querySelector(selector + ' #find-merge-button');
assert.isTrue(findMergeButton.disabled);
});
test(' > Find duplicates buttons must send and event when clicked',
function(done) {
window.addEventListener('findDuplicatesAction', function() {
done();
});
DetailsUI.render(mozContact, 2, true);
var findMergeButton =
document.querySelector(selector + ' #find-merge-button');
assert.isFalse(findMergeButton.disabled);
findMergeButton.click();
});
});
suite('Photo', function() {
test(' > Photo must be rendered properly', function() {
var photo = ContactPhotoHelper.getFullResolution(mozContact);
DetailsUI.calculateHash(photo, hash => {
var cover = document.querySelector('#cover-img');
assert.equal(cover.dataset.imgHash, hash);
});
});
});
});

View file

@ -0,0 +1,696 @@
'use strict';
/* global ActivityHandler */
/* global contacts */
/* global ContactsService */
/* global MockContactAllFields */
/* global MockContacts */
/* global MockContactsButtons */
/* global MockContactsListObj */
/* global MockDetailsDom */
/* global MockExtFb */
/* global Mockfb */
/* global MockMozNfc */
/* global MockContactsNfc */
/* global MocksHelper */
/* global MainNavigation */
/* global MockMozContacts */
/* global MockUtils */
/* global MockWebrtcClient */
/* global Normalizer */
/* export TAG_OPTIONS */
/* global triggerEvent */
/* exported SCALE_RATIO */
/* global utils */
/* global NFC */
/* exported _ */
//Avoiding lint checking the DOM file renaming it to .html
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/test/unit/mock_details_dom.js.html');
requireApp(
'communications/contacts/test/unit/webrtc-client/mock_webrtc_client.js');
require('/shared/js/text_normalizer.js');
require('/shared/js/contacts/import/utilities/misc.js');
require('/shared/js/contacts/utilities/dom.js');
require('/shared/js/contacts/utilities/templates.js');
requireApp('communications/contacts/test/unit/mock_event_listeners.js');
require('/shared/test/unit/mocks/mock_contact_all_fields.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/contacts/mock_contacts_buttons.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
requireApp('communications/contacts/js/views/details.js');
requireApp('communications/contacts/test/unit/mock_contacts_nfc.js');
requireApp('communications/contacts/test/unit/mock_cache.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
requireApp('communications/contacts/test/unit/mock_main_navigation.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
requireApp('communications/contacts/test/unit/mock_contacts_list_obj.js');
requireApp('communications/contacts/test/unit/mock_fb.js');
requireApp('communications/contacts/test/unit/mock_extfb.js');
requireApp('communications/contacts/test/unit/mock_activities.js');
requireApp('communications/contacts/test/unit/helper.js');
requireApp('communications/contacts/js/utilities/mozContact.js');
requireApp('communications/contacts/js/utilities/extract_params.js');
require('/shared/test/unit/mocks/mock_contact_photo_helper.js');
require('/shared/test/unit/mocks/mock_moz_contact.js');
require('/shared/test/unit/mocks/mock_moz_nfc.js');
var _ = function(key) { return key; },
subject,
container,
realL10n,
realOnLine,
realFormatDate,
dom,
contactDetails,
listContainer,
detailsName,
detailsNameText,
orgTitle,
phonesTemplate,
emailsTemplate,
addressesTemplate,
socialTemplate,
editContactButton,
cover,
favoriteMessage,
detailsInner,
TAG_OPTIONS,
realContacts,
realFb,
mockContact,
realContactsList,
mozL10nGetSpy,
header,
realListeners,
realNFC,
realMozNFC;
requireApp('communications/contacts/js/tag_optionsstem.js');
var SCALE_RATIO = 1;
var mocksHelperForDetailView = new MocksHelper([
'MainNavigation',
'ActivityHandler',
'LazyLoader',
'Cache',
'ContactsButtons',
'ContactPhotoHelper',
'mozContact',
'WebrtcClient'
]).init();
suite('Render contact', function() {
mocksHelperForDetailView.attachTestHelpers();
var isOnLine = true;
function navigatorOnLine() {
return isOnLine;
}
function setNavigatorOnLine(value) {
isOnLine = value;
}
suiteSetup(function() {
navigator.mozContacts = MockMozContacts;
realOnLine = Object.getOwnPropertyDescriptor(navigator, 'onLine');
realL10n = navigator.mozL10n;
realListeners = utils.listeners;
utils.listeners = MockUtils.listeners;
navigator.mozL10n = {
get: function get(key) {
return key;
},
DateTimeFormat: function() {
this.localeFormat = function(date, format) {
return date;
};
},
translate: function() {
}
};
mozL10nGetSpy = sinon.spy(navigator.mozL10n, 'get');
realFormatDate = utils.misc.formatDate;
utils.misc.formatDate = function(date) {
var offset = date.getTimezoneOffset() * 60 * 1000;
var normalizedDate = new Date(date.getTime() + offset);
return normalizedDate.toString();
};
Object.defineProperty(navigator, 'onLine', {
configurable: true,
get: navigatorOnLine,
set: setNavigatorOnLine
});
realContactsList = contacts.List;
contacts.List = MockContactsListObj;
realContacts = window.Contacts;
window.Contacts = MockContacts;
realNFC = window.NFC;
window.NFC = MockContactsNfc;
realMozNFC = window.navigator.mozNfc;
window.navigator.mozNfc = MockMozNfc;
realFb = window.fb;
window.fb = Mockfb;
window.ExtServices = MockExtFb;
dom = document.createElement('section');
dom.id = 'view-contact-details';
dom.innerHTML = MockDetailsDom;
container = dom.querySelector('#details-list');
subject = contacts.Details;
utils.listeners.dom = dom;
subject.init(dom);
contactDetails = dom.querySelector('#contact-detail');
listContainer = dom.querySelector('#details-list');
detailsName = dom.querySelector('#contact-name-title');
detailsNameText = dom.querySelector('#contact-name-title bdi');
orgTitle = dom.querySelector('#org-title');
phonesTemplate = dom.querySelector('#phone-details-template-\\#i\\#');
emailsTemplate = dom.querySelector('#email-details-template-\\#i\\#');
addressesTemplate = dom.querySelector('#address-details-template-\\#i\\#');
socialTemplate = dom.querySelector('#social-template-\\#i\\#');
editContactButton = dom.querySelector('#edit-contact-button');
cover = dom.querySelector('#cover-img');
detailsInner = dom.querySelector('#contact-detail-inner');
favoriteMessage = dom.querySelector('#toggle-favorite');
header = dom.querySelector('#details-view-header');
});
suiteTeardown(function() {
window.Contacts = realContacts;
contacts.List = realContactsList;
window.fb = realFb;
mozL10nGetSpy.restore();
window.mozL10n = realL10n;
window.NFC = realNFC;
window.navigator.mozNfc = realMozNFC;
utils.listeners = realListeners;
if (realOnLine) {
Object.defineProperty(navigator, 'onLine', realOnLine);
}
utils.misc.formatDate = realFormatDate;
});
setup(function() {
mockContact = new MockContactAllFields(true);
subject.setContact(mockContact);
window.set;
});
teardown(function() {
container.innerHTML = '';
mozL10nGetSpy.reset();
});
suite('Render name', function() {
test('with name', function() {
subject.render(null, TAG_OPTIONS);
assert.equal(detailsNameText.textContent, mockContact.name[0]);
});
test('without name, with phone', function() {
var contactWoName = new MockContactAllFields(true);
contactWoName.name = null;
subject.setContact(contactWoName);
subject.render(null, TAG_OPTIONS);
assert.equal(detailsNameText.textContent, contactWoName.tel[0].value);
});
test('without name, without phone, with email', function() {
var contactWoName = new MockContactAllFields(true);
contactWoName.name = null;
contactWoName.tel = null;
subject.setContact(contactWoName);
subject.render(null, TAG_OPTIONS);
assert.equal(detailsNameText.textContent, contactWoName.email[0].value);
});
test('no name, no phone, no email', function() {
var contactWoName = new MockContactAllFields(true);
contactWoName.name = null;
contactWoName.tel = null;
contactWoName.email = null;
subject.setContact(contactWoName);
subject.render(null, TAG_OPTIONS);
assert.notEqual(detailsNameText.textContent, '');
assert.isTrue(mozL10nGetSpy.calledWith('noName'));
});
});
suite('Render favorite', function() {
test('with favorite contact', function() {
subject.render(null, TAG_OPTIONS);
assert.isTrue(header.classList.contains('favorite'));
});
test('without favorite contact', function() {
var contactWoFav = new MockContactAllFields(true);
contactWoFav.category = [];
subject.setContact(contactWoFav);
subject.render(null, TAG_OPTIONS);
assert.isFalse(header.classList.contains('favorite'));
});
test('change in favorite not render the window', function(done) {
var contactWoPhoto = new MockContactAllFields();
contactWoPhoto.photo = null;
// Stub so save is working as if it was successful
this.sinon.stub(ContactsService, 'save', function(contact, cb) {
cb();
});
// Stub find, so request is working
this.sinon.stub(navigator.mozContacts, 'find', function(options) {
return {
onsuccess: function(){},
onerror: function() {}
};
});
subject.setContact(contactWoPhoto);
subject.render(null, TAG_OPTIONS);
var spy = sinon.spy(subject, 'toggleFavorite');
subject.toggleFavorite();
spy.lastCall.returnValue.then(function(value) {
assert.isTrue(value);
}).then(done, done);
});
});
suite('Render org', function() {
test('with org', function() {
subject.render(null, TAG_OPTIONS);
assert.equal(mockContact.org[0], orgTitle.textContent);
assert.equal(false, orgTitle.classList.contains('hide'));
});
test('without org', function() {
var contactWoOrg = new MockContactAllFields(true);
contactWoOrg.org = [];
subject.setContact(contactWoOrg);
subject.render(null, TAG_OPTIONS);
assert.equal('', orgTitle.textContent);
assert.equal(true, orgTitle.classList.contains('hide'));
});
});
suite('Render social', function() {
test('Share button must be renderer properly', function() {
subject.render(null, TAG_OPTIONS);
assert.include(container.innerHTML, 'social-template');
assert.isFalse(container.querySelector('#share_button').
classList.contains('hide'));
});
});
suite('Render ContactsButtons', function() {
setup(function() {
this.sinon.stub(MockContactsButtons, 'renderPhones');
this.sinon.stub(MockContactsButtons, 'renderEmails');
subject.render(null, TAG_OPTIONS);
});
test('initializes ContactsButtons', function() {
this.sinon.stub(MockContactsButtons, 'init');
subject.init(dom);
sinon.assert.calledWith(MockContactsButtons.init, listContainer,
contactDetails, ActivityHandler);
});
test('calls renderPhones', function() {
sinon.assert.calledWith(MockContactsButtons.renderPhones, mockContact);
});
test('calls renderEmails', function() {
sinon.assert.calledWith(MockContactsButtons.renderEmails, mockContact);
});
});
suite('Render addresses', function() {
test('in correct position', function() {
subject.render(null, TAG_OPTIONS);
assert.include(container.innerHTML, 'address-details-template-0');
var li = container.querySelector('#address-details-template-0');
assert.equal(li.nextSibling.id, 'dates-template-1');
});
test('with 1 address', function() {
subject.render(null, TAG_OPTIONS);
assert.include(container.innerHTML, 'address-details-template-0');
var address0 = mockContact.adr[0];
assert.include(container.innerHTML,
Normalizer.escapeHTML(address0.countryName, true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(address0.locality, true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(address0.postalCode, true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(address0.streetAddress, true));
});
test('with no addresses', function() {
var contactWoAddress = new MockContactAllFields(true);
contactWoAddress.adr = [];
subject.setContact(contactWoAddress);
subject.render(null, TAG_OPTIONS);
assert.equal(-1, container.innerHTML.indexOf('address-details-template'));
});
test('with null addresses', function() {
var contactWoAddress = new MockContactAllFields(true);
contactWoAddress.adr = null;
subject.setContact(contactWoAddress);
subject.render(null, TAG_OPTIONS);
assert.equal(-1, container.innerHTML.indexOf('address-details-template'));
});
test('with more than 1 address', function() {
var contactMultAddress = new MockContactAllFields(true);
contactMultAddress.adr[1] = contactMultAddress.adr[0];
for (var elem in contactMultAddress.adr[1]) {
var currentElem = contactMultAddress.adr[1][elem] + 'dup';
contactMultAddress.adr[1][elem] = currentElem;
}
subject.setContact(contactMultAddress);
subject.render(null, TAG_OPTIONS);
assert.include(container.innerHTML, 'address-details-template-0');
assert.include(container.innerHTML, 'address-details-template-1');
var address0 = contactMultAddress.adr[0];
var address1 = contactMultAddress.adr[1];
assert.include(container.innerHTML,
Normalizer.escapeHTML(address0.countryName, true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(address0.locality, true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(address0.postalCode, true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(address0.streetAddress, true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(address1.countryName, true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(address1.locality, true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(address1.postalCode, true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(address1.streetAddress, true));
var toCheck = container.innerHTML;
assert.equal(-1, toCheck.indexOf('address-details-template-2'));
});
});
suite('Render dates', function() {
function assertRenderedDate(date, dateBlock) {
var offset = date.getTimezoneOffset() * 60 * 1000;
var targetDate = new Date(date.getTime() + offset);
assert.equal(dateBlock.querySelector('strong').textContent,
targetDate.toString());
}
test('with bday', function() {
subject.render(null, TAG_OPTIONS);
var bdayBlock = container.querySelector('#dates-template-1');
assert.isNotNull(bdayBlock);
// Ensuring timezone correctly treated (Bug 880775)
assertRenderedDate(mockContact.bday, bdayBlock);
});
test('with anniversary', function() {
var contactWithAnn = new MockContactAllFields(true);
contactWithAnn.bday = null;
contactWithAnn.anniversary = new Date(0);
subject.setContact(contactWithAnn);
subject.render(null, TAG_OPTIONS);
var dateBlock = container.querySelector('#dates-template-1');
assert.isNotNull(dateBlock);
assertRenderedDate(contactWithAnn.anniversary, dateBlock);
});
test('with bday and anniversary', function() {
var contactWithAll = new MockContactAllFields(true);
contactWithAll.anniversary = new Date(0);
subject.setContact(contactWithAll);
subject.render(null, TAG_OPTIONS);
var fields = ['bday', 'anniversary'];
for (var j = 0; j < fields.length; j++) {
var dateBlock = container.querySelector('#dates-template-' + (j + 1));
assert.isNotNull(dateBlock);
assertRenderedDate(contactWithAll[fields[j]], dateBlock);
}
});
test('without dates', function() {
var contactWoDates = new MockContactAllFields(true);
contactWoDates.bday = null;
contactWoDates.anniversary = null;
subject.setContact(contactWoDates);
subject.render(null, TAG_OPTIONS);
assert.equal(-1, container.innerHTML.indexOf('dates'));
});
});
suite('Render notes', function() {
test('with 1 note', function() {
subject.render(null, TAG_OPTIONS);
assert.include(container.innerHTML, 'note-details-template-0');
assert.include(container.innerHTML,
Normalizer.escapeHTML(mockContact.note[0], true));
});
test('with no notes', function() {
var contactWoNote = new MockContactAllFields(true);
contactWoNote.note = [];
subject.setContact(contactWoNote);
subject.render(null, TAG_OPTIONS);
assert.equal(-1, container.innerHTML.indexOf('note-details-template'));
});
test('with null notes', function() {
var contactWoNote = new MockContactAllFields(true);
contactWoNote.note = null;
subject.setContact(contactWoNote);
subject.render(null, TAG_OPTIONS);
assert.equal(-1, container.innerHTML.indexOf('note-details-template'));
});
test('with more than 1 note', function() {
var contactMultNote = new MockContactAllFields(true);
contactMultNote.note[1] = String(contactMultNote.note[0]);
subject.setContact(contactMultNote);
subject.render(null, TAG_OPTIONS);
assert.include(container.innerHTML, 'note-details-template-0');
assert.include(container.innerHTML, 'note-details-template-1');
assert.include(container.innerHTML,
Normalizer.escapeHTML(contactMultNote.note[0], true));
assert.include(container.innerHTML,
Normalizer.escapeHTML(contactMultNote.note[1], true));
assert.equal(-1, container.innerHTML.indexOf('note-details-template-2'));
});
});
suite('Render duplicate section', function() {
test('Total contacts = 1 -> Find merge button disabled ', function() {
MockContactsListObj.total = 1;
subject.render(null, TAG_OPTIONS);
assert.isTrue(container.querySelector('#find-merge-button').disabled);
});
test('Total contacts > 1 -> Find merge button enabled ', function() {
MockContactsListObj.total = 2;
subject.render(null, TAG_OPTIONS);
assert.isFalse(container.querySelector('#find-merge-button').disabled);
});
test('FB Imported Contact -> Find Duplicates does not appear', function() {
window.fb.setIsFbContact(true);
window.fb.setIsFbLinked(false);
subject.render(null, TAG_OPTIONS);
assert.isNull(container.querySelector('#find-merge-button'));
});
test('FB Linked Contact -> Find Duplicates appears', function() {
window.fb.setIsFbContact(true);
window.fb.setIsFbLinked(true);
subject.render(null, TAG_OPTIONS);
assert.isNotNull(container.querySelector('#find-merge-button'));
window.fb.setIsFbContact(false);
window.fb.setIsFbLinked(false);
});
});
suite('Render in read only mode', function() {
setup(function() {
subject.render(null, TAG_OPTIONS, true);
});
test('> editing button is disabled if we are in read only', function() {
assert.isTrue(editContactButton.classList.contains('hide'));
});
test('> link and share buttons are disabled in reado only', function() {
assert.isTrue(socialTemplate.classList.contains('hide'));
});
});
suite('Render photos', function() {
test('without photo', function() {
subject.render(null, TAG_OPTIONS);
assert.equal(cover.style.backgroundImage, '');
assert.equal(cover.style.overflow, 'auto');
assert.equal(contactDetails.style.transform, '');
assert.isTrue(contactDetails.classList.contains('no-photo'));
assert.isFalse(contactDetails.classList.contains('up'));
});
test('with null photo', function() {
var contactWoPhoto = new MockContactAllFields();
contactWoPhoto.photo = null;
subject.setContact(contactWoPhoto);
subject.render(null, TAG_OPTIONS);
assert.equal(cover.style.backgroundImage, '');
assert.equal(cover.style.overflow, 'auto');
assert.equal(contactDetails.style.transform, '');
assert.isTrue(contactDetails.classList.contains('no-photo'));
assert.isFalse(contactDetails.classList.contains('up'));
});
// See bug 946064
test(
'Adding one with photo and then adding the same (photo is kept as it is)',
function(done) {
var contact = new MockContactAllFields();
subject.setContact(contact);
var observer = new MutationObserver(function() {
assert.isTrue(contactDetails.classList.contains('up'));
// assert.include worked only for string and arrays!!
// in new version chaijs fail
//assert.include(dom.innerHTML, contact.photo[0]);
observer.disconnect();
var spy = sinon.spy(utils.dom, 'updatePhoto');
var observer2 = new MutationObserver(function() {
observer2.disconnect();
assert.equal(spy.callCount, 0);
done();
});
observer2.observe(cover, {
attributes: true,
attributeFilter: ['data-photo-ready']
});
subject.render(null, TAG_OPTIONS);
});
observer.observe(cover, {
attributes: true,
attributeFilter: ['data-img-hash']
});
subject.render(null, TAG_OPTIONS);
});
});
suite('NFC activation', function() {
setup(function() {
this.sinon.spy(NFC, 'startListening');
this.sinon.spy(NFC, 'stopListening');
});
test('> start listening when render a contact', function() {
subject.render(null, TAG_OPTIONS);
sinon.assert.calledOnce(NFC.startListening);
});
test('> stop listening when opening edit mode', function() {
// subject.render(null, TAG_OPTIONS);
// sinon.assert.calledOnce(NFC.startListening);
editContactButton.click();
sinon.assert.calledOnce(NFC.stopListening);
});
test('> stop listening when closing details', function() {
// subject.render(null, TAG_OPTIONS);
// sinon.assert.calledOnce(NFC.startListening);
triggerEvent(header, 'action');
sinon.assert.calledOnce(NFC.stopListening);
});
});
suite('> Handle back button', function() {
setup(function () {
this.sinon.spy(MockWebrtcClient, 'stop');
this.sinon.spy(window.ActivityHandler, 'postCancel');
this.sinon.spy(MainNavigation, 'back');
});
test('> going back from details', function () {
triggerEvent(header, 'action');
sinon.assert.calledOnce(MockWebrtcClient.stop);
sinon.assert.notCalled(ActivityHandler.postCancel);
sinon.assert.calledOnce(MainNavigation.back);
});
test('> going back from details during an activity', function () {
ActivityHandler.currentlyHandling = true;
triggerEvent(header, 'action');
sinon.assert.calledOnce(MockWebrtcClient.stop);
sinon.assert.calledOnce(ActivityHandler.postCancel);
sinon.assert.notCalled(MainNavigation.back);
ActivityHandler.currentlyHandling = false;
});
test('> going back from details during an IMPORT activity', function () {
ActivityHandler.currentlyHandling = true;
ActivityHandler.activityName = 'import';
triggerEvent(header, 'action');
sinon.assert.calledOnce(MockWebrtcClient.stop);
sinon.assert.notCalled(ActivityHandler.postCancel);
sinon.assert.calledOnce(MainNavigation.back);
ActivityHandler.currentlyHandling = false;
ActivityHandler.activityName = 'view';
});
suite('back_to_previous_tab set', function() {
setup(function() {
window.location.hash = '#nothing?back_to_previous_tab=1';
});
teardown(function() {
window.location.hash = '';
});
test('> should not navigate back', function() {
triggerEvent(header, 'action');
sinon.assert.notCalled(MainNavigation.back);
});
});
});
});

View file

@ -0,0 +1,165 @@
/* global LazyLoader, FormController, FormUI */
'use strict';
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/js/lazy_loader.js');
requireApp('communications/contacts/views/form/js/boot.js');
suite('FORM (#new/update) - Boot', function() {
var stub;
suiteSetup(function() {
stub = sinon.stub(LazyLoader, 'load', function(files, callback) {
callback();
});
});
suiteTeardown(function() {
LazyLoader.load.restore();
});
setup(function() {
loadBodyHTML('/contacts/views/form/form.html');
});
teardown(function() {
document.body.innerHTML = '';
});
suite('DOMContentLoaded', function() {
setup(function() {
window.dispatchEvent(new CustomEvent('DOMContentLoaded'));
});
test(' > LazyLoader must be called 2 times', function() {
// We must have 2 calls to LazyLoader
assert.isTrue(stub.calledTwice);
});
test(' > First call must ensure localization', function() {
// First of all we need to bring l10n library for localization
var firstCall = stub.getCall(0);
assert.isTrue(Array.isArray(firstCall.args[0]));
assert.equal(firstCall.args[0][0], '/shared/js/l10n.js');
});
test(' > Second call must load the template', function() {
// Secondly we lazy load the panel, so the param must be the element
// of the DOM to fill with the template
var secondCall = stub.getCall(1);
assert.equal(secondCall.args[0][0].id, 'view-contact-form');
});
});
suite('Loaded', function() {
var realFormController, realFormUI, realMozSetMessage;
suiteSetup(function() {
realFormController = window.FormController;
realFormUI = window.FormUI;
window.FormController = {
init: function foo() {}
};
window.FormUI = {
init: function foo() {}
};
realMozSetMessage = navigator.mozSetMessageHandler;
navigator.mozSetMessageHandler = function foo() {};
});
suiteTeardown(function() {
window.FormController = realFormController;
window.FormUI = realFormUI;
navigator.mozSetMessageHandler = realMozSetMessage;
realFormController = null;
realFormUI = null;
realMozSetMessage = null;
});
test(' > Controller is initialized', function() {
var formSpy = this.sinon.spy(FormController, 'init');
window.dispatchEvent(new CustomEvent('load'));
assert.isTrue(formSpy.calledOnce);
});
test(' > UI is initialized', function() {
var formUISpy = this.sinon.spy(FormUI, 'init');
window.dispatchEvent(new CustomEvent('load'));
assert.isTrue(formUISpy.calledOnce);
});
});
suite('Activity', function() {
var realFormController, realFormUI, realMozSetMessage;
var fakeActivity = {
source: {
data: {
params: ['test']
}
}
};
suiteSetup(function() {
realFormController = window.FormController;
realFormUI = window.FormUI;
window.FormController = {
init: function init() {},
setActivity: function setActivity() {}
};
window.FormUI = {
init: function init() {},
render: function render() {}
};
});
suiteTeardown(function() {
window.FormController = realFormController;
window.FormUI = realFormUI;
realFormController = null;
realFormUI = null;
realMozSetMessage = null;
});
test(' > Listener to activity is set properly', function(done) {
this.sinon.stub(navigator, 'mozSetMessageHandler', function() {
done();
});
window.dispatchEvent(new CustomEvent('load'));
});
test(' > UI is updated properly with activity params', function(done) {
var formUIRenderSpy = this.sinon.spy(FormUI, 'render');
this.sinon.stub(navigator, 'mozSetMessageHandler',
function(name, callback) {
callback(fakeActivity);
assert.isTrue(formUIRenderSpy.called);
assert.equal(
formUIRenderSpy.getCall(0).args[0],
fakeActivity.source.data.params
);
done();
}
);
window.dispatchEvent(new CustomEvent('load'));
});
test(' > Activity is cached in Controller', function(done) {
var formActivitySpy = this.sinon.spy(FormController, 'setActivity');
this.sinon.stub(navigator, 'mozSetMessageHandler',
function(name, callback) {
callback(fakeActivity);
assert.isTrue(formActivitySpy.called);
done();
}
);
window.dispatchEvent(new CustomEvent('load'));
});
});
});

View file

@ -0,0 +1,121 @@
/* global LazyLoader, FormController, ContactsService, MockL10n, mozContact */
/* global Matcher, MatchService */
'use strict';
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/js/lazy_loader.js');
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/js/match_service.js');
require('/shared/js/text_normalizer.js');
require('/shared/js/simple_phone_matcher.js');
require('/shared/js/contacts/contacts_matcher.js');
require('/shared/js/contacts/import/utilities/misc.js');
requireApp('communications/contacts/views/form/js/form_controller.js');
suite('FormController', function() {
var realMozL10n;
var testContact;
var stubLazyLoader;
var stubContactServiceSave;
suiteSetup(function() {
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
FormController.init();
});
suiteTeardown(function() {
navigator.mozL10n = realMozL10n;
realMozL10n = null;
});
function dispatchSaveEvent() {
window.dispatchEvent(new CustomEvent('save-contact',
{detail: testContact}));
}
setup(function() {
testContact = new mozContact();
testContact.givenName = ['Test'];
stubContactServiceSave = this.sinon.stub(ContactsService, 'save',
function(contact, onerror) {});
stubLazyLoader = this.sinon.stub(LazyLoader, 'load',
function(files, callback) {
callback();
});
});
teardown(function() {
stubLazyLoader = null;
stubLazyLoader = null;
stubContactServiceSave = null;
testContact = null;
});
suite('Close button', function() {
test(' > must call close when an event is recived', function(done) {
var spy = this.sinon.spy(window.history, 'back');
window.addEventListener('close-ui', function() {
assert.isTrue(spy.calledOnce);
done();
});
window.dispatchEvent(new CustomEvent('close-ui'));
});
});
suite('Save button', function() {
setup(function() {
dispatchSaveEvent();
});
test(' > LazyLoader must be called 2 times', function() {
assert.isTrue(stubLazyLoader.calledTwice);
});
test(' > First call must ensure match service', function() {
// First of all we need to bring l10n library for localization
var firstCall = stubLazyLoader.getCall(0);
assert.isTrue(Array.isArray(firstCall.args[0]));
assert.equal(firstCall.args[0][0], '/contacts/style/match_service.css');
assert.equal(firstCall.args[0][1], '/contacts/js/match_service.js');
});
test(' > Second call must ensure other dependencies', function() {
var secondCall = stubLazyLoader.getCall(1);
assert.isTrue(Array.isArray(secondCall.args[0]));
assert.equal(secondCall.args[0][0], '/shared/js/text_normalizer.js');
assert.equal(secondCall.args[0][1], '/shared/js/simple_phone_matcher.js');
assert.equal(secondCall.args[0][2],
'/shared/js/contacts/contacts_matcher.js');
});
});
suite('On match contacts', function() {
var stubMatcher;
setup(function() {
stubMatcher = this.sinon.stub(Matcher, 'match',
function(contact, mode, callbacks) {
callbacks.onmatch();
});
});
teardown(function() {
stubMatcher = null;
});
test(' > When matching contacts showDuplicatesContacts must be called',
function() {
var stubShowDuplicates =
this.sinon.stub(MatchService, 'showDuplicateContacts');
dispatchSaveEvent();
assert.isTrue(stubShowDuplicates.called);
assert.isFalse(stubContactServiceSave.called);
});
});
});

View file

@ -0,0 +1,495 @@
/* global LazyLoader, FormUI, MockL10n */
'use strict';
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/js/lazy_loader.js');
require('/shared/elements/config.js');
require('/shared/js/component_utils.js');
require('/shared/elements/gaia_subheader/script.js');
require('/shared/elements/gaia-header/dist/gaia-header.js');
requireApp('communications/contacts/js/utilities/performance_helper.js');
require('/shared/js/l10n.js');
requireApp('communications/contacts/js/navigation.js');
requireApp('communications/contacts/services/contacts.js');
require('/shared/js/contact_photo_helper.js');
require('/shared/js/contacts/utilities/templates.js');
requireApp('communications/contacts/js/contacts_tag.js');
requireApp('communications/contacts/js/tag_options.js');
require('/shared/js/text_normalizer.js');
require('/shared/js/contacts/import/utilities/status.js');
require('/shared/js/contacts/utilities/dom.js');
require('/shared/js/contacts/import/utilities/misc.js');
requireApp('communications/contacts/views/form/js/form_ui.js');
/*
* NOTE: How tests are working:
* Every suite will try to test all actions, step by step, that
* you can execute in the form.
*
* Take into account that all tests within a suite rely on the former
* one, so we are keeping the state (in order to execute several UI
* actions in a linear manner).
*
* Keep in mind this behaviour!
*
*/
suite('FormUI', function() {
var realMozL10n;
suiteSetup(function(done) {
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
// Load HTML
loadBodyHTML('/contacts/views/form/form.html');
// Add hook to template to "head"
var importHook = document.createElement('link');
importHook.id = 'form-import-link';
importHook.setAttribute('rel', 'import');
importHook.setAttribute('href', '/contacts/elements/form.html');
document.head.appendChild(importHook);
// Fill the HTML
LazyLoader.load(
[
'/contacts/js/tag_options.js',
document.getElementById('view-contact-form')
],
function() {
FormUI.init();
done();
}
);
});
suiteTeardown(function() {
navigator.mozL10n = realMozL10n;
realMozL10n = null;
var importHook =
document.querySelector('link[rel=import][id="form-import-link"]');
document.head.removeChild(importHook);
document.body.innerHTML = '';
window.FormUI = null;
});
suite('Close button', function() {
test(' > must dispatch an event when clicked', function(done) {
window.addEventListener('close-ui', function() {
done();
});
var clickEvent = new CustomEvent('action');
document.querySelector('#contact-form-header').dispatchEvent(clickEvent);
});
});
suite('Phone number', function() {
var contact = {
tel: ['123456789']
};
function getPhoneInputs() {
var inputs =
document.querySelectorAll(
'input[data-field="value"]:not([value="#value#"])'
);
inputs = Array.prototype.filter.call(inputs, function(element) {
return element.id.startsWith('number');
});
return inputs;
}
test(' > Add one phone number by "render" method', function() {
FormUI.render(contact);
var inputsRendered = getPhoneInputs();
assert.equal(inputsRendered.length, 1);
assert.equal(inputsRendered[0].value, contact.tel[0]);
});
test(' > Save button must be enabled', function() {
var saveButton = document.querySelector('#save-button');
assert.equal(saveButton.getAttribute('disabled'), null);
});
test(' > Reset in given phone number', function() {
var inputs = getPhoneInputs();
// Now we launch the event
var resetButton = inputs[0].parentNode.querySelector('button');
var touchstart = 'ontouchstart' in window ? 'touchstart' : 'mousedown';
var customEvent = new CustomEvent(
touchstart,
{
bubbles: true,
cancelable: true
}
);
resetButton.dispatchEvent(customEvent);
// Input must be empty after tapping on 'reset'
assert.equal(inputs[0].value, '');
});
test(' > Add phone number manually', function() {
document.getElementById('add-new-phone').click();
var inputs = getPhoneInputs();
// After tapping we must have 2 entries in the form
assert.equal(inputs.length, 2);
});
test(' > Remove phone numbers', function() {
var deleteButtons =
document.querySelectorAll(
'button[data-action="delete"][data-type="tel"]'
);
var touchstart = 'ontouchstart' in window ? 'touchstart' : 'mousedown';
var customEvent = new CustomEvent(
touchstart,
{
bubbles: true,
cancelable: true
}
);
for (var i = 0; i < deleteButtons.length; i++) {
deleteButtons[i].dispatchEvent(customEvent);
}
var inputs = getPhoneInputs();
// After cleaning both entries, we must have no phone number
// input in the form
assert.equal(inputs.length, 0);
});
test(' > Save button must be disabled', function() {
var saveButton = document.querySelector('#save-button');
assert.equal(saveButton.getAttribute('disabled'), 'disabled');
});
});
suite('Email', function() {
var contact = {
email: ['manolete@mozilla.com']
};
function getEmailInputs() {
var inputs =
document.querySelectorAll(
'input[data-field="value"]:not([value="#value#"])'
);
inputs = Array.prototype.filter.call(
inputs,
function(element) {
return element.id.startsWith('email');
}
);
return inputs;
}
test(' > Add one email by "render" method', function() {
FormUI.render(contact);
var inputsRendered = getEmailInputs();
assert.equal(inputsRendered.length, 1);
assert.equal(inputsRendered[0].value, contact.email[0]);
});
test(' > Save button must be enabled', function() {
var saveButton = document.querySelector('#save-button');
assert.equal(saveButton.getAttribute('disabled'), null);
});
test(' > Reset in given email', function() {
var inputs = getEmailInputs();
// Now we launch the event
var resetButton = inputs[0].parentNode.querySelector('button');
var touchstart = 'ontouchstart' in window ? 'touchstart' : 'mousedown';
var customEvent = new CustomEvent(
touchstart,
{
bubbles: true,
cancelable: true
}
);
resetButton.dispatchEvent(customEvent);
// Input must be empty after tapping on 'reset'
assert.equal(inputs[0].value, '');
});
test(' > Add email manually', function() {
document.getElementById('add-new-email').click();
var inputs = getEmailInputs();
assert.equal(inputs.length, 2);
});
test(' > Remove emails', function() {
var deleteButtons =
document.querySelectorAll(
'button[data-action="delete"][data-type="email"]'
);
var touchstart = 'ontouchstart' in window ? 'touchstart' : 'mousedown';
var customEvent = new CustomEvent(
touchstart,
{
bubbles: true,
cancelable: true
}
);
for (var i = 0; i < deleteButtons.length; i++) {
deleteButtons[i].dispatchEvent(customEvent);
}
var inputs = getEmailInputs();
// After cleaning both entries, we must have no phone number
// input in the form
assert.equal(inputs.length, 0);
});
test(' > Save button must be disabled', function() {
var saveButton = document.querySelector('#save-button');
assert.equal(saveButton.getAttribute('disabled'), 'disabled');
});
});
/*
* TODO Add more tests when #update will be added in order to
* ensure all info from a Contact is rendered properly.
*/
suite('Address', function() {
function getAddressInputs() {
var inputs =
document.querySelectorAll(
'input[data-field="streetAddress"]:not([value="#streetAddress#"])'
);
inputs = Array.prototype.filter.call(inputs, function(element) {
return element.id.startsWith('streetAddress');
});
return inputs;
}
test(' > Save button must be disabled before adding address', function() {
var saveButton = document.querySelector('#save-button');
assert.equal(saveButton.getAttribute('disabled'), 'disabled');
});
test(' > Add address manually', function() {
document.getElementById('add-new-address').click();
var inputs = getAddressInputs();
assert.equal(inputs.length, 1);
});
test(' > Save button must be enabled after tapping text', function() {
var inputs = getAddressInputs();
inputs[0].value = 'Address, City';
var inputEvent = new CustomEvent(
'input',
{
bubbles: true,
cancelable: true
}
);
inputs[0].dispatchEvent(inputEvent);
var saveButton = document.querySelector('#save-button');
assert.equal(saveButton.getAttribute('disabled'), null);
});
test(' > Remove address', function() {
var deleteButtons =
document.querySelectorAll('button[data-action="delete"]');
var touchstart = 'ontouchstart' in window ? 'touchstart' : 'mousedown';
var customEvent = new CustomEvent(
touchstart,
{
bubbles: true,
cancelable: true
}
);
for (var i = 0; i < deleteButtons.length; i++) {
deleteButtons[i].dispatchEvent(customEvent);
}
var inputs = getAddressInputs();
// After cleaning both entries, we must have no phone number
// input in the form
assert.equal(inputs.length, 0);
});
test(' > Save button must be disabled when deleting address', function() {
var saveButton = document.querySelector('#save-button');
assert.equal(saveButton.getAttribute('disabled'), 'disabled');
});
});
suite('Comment/Note', function() {
function getCommentInputs() {
var inputs = document.querySelectorAll('textarea:not([id="note_#i#"])');
inputs = Array.prototype.filter.call(inputs, function(element) {
return element.getAttribute('name').startsWith('comment');
});
return inputs;
}
test(' > Save button must be disabled before adding comment', function() {
var saveButton = document.querySelector('#save-button');
assert.equal(saveButton.getAttribute('disabled'), 'disabled');
});
test(' > Add comment manually', function() {
document.getElementById('add-new-note').click();
var inputs = getCommentInputs();
assert.equal(inputs.length, 1);
});
test(' > Save button must *NOT* be enabled after tapping text', function() {
var inputs = getCommentInputs();
inputs[0].value = 'This is a note';
var inputEvent = new CustomEvent(
'input',
{
bubbles: true,
cancelable: true
}
);
inputs[0].dispatchEvent(inputEvent);
var saveButton = document.querySelector('#save-button');
assert.equal(saveButton.getAttribute('disabled'), 'disabled');
});
test(' > Remove address', function() {
var deleteButtons =
document.querySelectorAll('button[data-action="delete"]');
var touchstart = 'ontouchstart' in window ? 'touchstart' : 'mousedown';
var customEvent = new CustomEvent(
touchstart,
{
bubbles: true,
cancelable: true
}
);
for (var i = 0; i < deleteButtons.length; i++) {
deleteButtons[i].dispatchEvent(customEvent);
}
var inputs = getCommentInputs();
// After cleaning both entries, we must have no phone number
// input in the form
assert.equal(inputs.length, 0);
});
});
suite('Date', function() {
function getDateInputs() {
var selector = 'input[type="date"]:not([id="date_#i#"])';
return document.querySelectorAll(selector);
}
test(' > Save button must be disabled before adding date', function() {
var saveButton = document.querySelector('#save-button');
assert.equal(saveButton.getAttribute('disabled'), 'disabled');
});
test(' > Add Date manually. Must be "birthday"', function() {
document.getElementById('add-new-date').click();
var inputs = getDateInputs();
assert.equal(inputs[0].dataset.field, 'birthday');
assert.equal(inputs.length, 1);
});
test(' > Add a second Date manually. Must be "Anniversary"', function() {
document.getElementById('add-new-date').click();
var inputs = getDateInputs();
assert.equal(inputs[1].dataset.field, 'anniversary');
assert.equal(inputs.length, 2);
});
test(' > We can not add more Dates! Max is 2.', function() {
// Click several times
document.getElementById('add-new-date').click();
document.getElementById('add-new-date').click();
document.getElementById('add-new-date').click();
document.getElementById('add-new-date').click();
// Inputs are not growing
var inputs = getDateInputs();
assert.equal(inputs.length, 2);
// Button for adding must be disabled
assert.isTrue(document.querySelector('#add-new-date').disabled);
});
test(' > Remove Dates', function() {
var deleteButtons =
document.querySelectorAll('button[data-action="delete"]');
var touchstart = 'ontouchstart' in window ? 'touchstart' : 'mousedown';
var customEvent = new CustomEvent(
touchstart,
{
bubbles: true,
cancelable: true
}
);
for (var i = 0; i < deleteButtons.length; i++) {
deleteButtons[i].dispatchEvent(customEvent);
}
var inputs = getDateInputs();
// After cleaning both entries, we must have no dates
// input in the form
assert.equal(inputs.length, 0);
});
});
suite('Save button', function() {
var contact = {
tel: ['123456789']
};
test(' > must dispatch an event when clicked', function(done) {
FormUI.render(contact);
window.addEventListener('save-contact', function() {
done();
});
var clickEvent = new CustomEvent('click', {'detail': contact});
document.querySelector('#save-button').dispatchEvent(clickEvent);
});
});
});

View file

@ -0,0 +1,969 @@
'use strict';
/* global contacts */
/* global ConfirmDialog */
/* global LazyLoader */
/* global MockContactAllFields */
/* global MockContactsSearch */
/* global MockExtServices */
/* global Mockfb */
/* global MockFormDom */
/* global MocksHelper */
/* global MockMozContactsObj */
/* global MockThumbnailImage */
/* global Matcher */
/* global Search */
/* global utils */
/* exported _ */
require('/shared/test/unit/mocks/mock_contact_all_fields.js');
require('/shared/js/text_normalizer.js');
require('/shared/js/lazy_loader.js');
require('/shared/js/contacts/import/utilities/misc.js');
require('/shared/js/contacts/utilities/dom.js');
require('/shared/js/contacts/utilities/templates.js');
require('/shared/js/contacts/utilities/event_listeners.js');
//Avoiding lint checking the DOM file renaming it to .html
requireApp('communications/contacts/test/unit/mock_form_dom.js.html');
requireApp('communications/contacts/js/contacts_tag.js');
requireApp('communications/contacts/js/views/form.js');
requireApp('communications/contacts/js/utilities/mozContact.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
requireApp('communications/contacts/test/unit/mock_main_navigation.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
requireApp('communications/contacts/test/unit/mock_external_services.js');
requireApp('communications/contacts/test/unit/mock_fb.js');
requireApp('communications/contacts/test/unit/mock_contacts_search.js');
requireApp('communications/contacts/test/unit/mock_confirm_dialog.js');
requireApp('communications/contacts/test/unit/mock_image_thumbnail.js');
require('/shared/test/unit/mocks/mock_contact_photo_helper.js');
var subject,
_,
realL10n,
realFb,
realThumbnailImage,
mockContact,
footer,
ActivityHandler;
var MOCK_NUMERIC_DATE_STRING =
new Date(Date.UTC(1970, 0, 1)).toLocaleString(navigator.languages, {
year: 'numeric',
month: 'numeric',
day: 'numeric'
}
);
realL10n = navigator.mozL10n;
navigator.mozL10n = {
get: function get(key) {
return key;
},
};
window._ = navigator.mozL10n.get;
requireApp('communications/contacts/js/tag_options.js');
var mocksForm = new MocksHelper([
'MainNavigation',
'Contacts',
'ConfirmDialog',
'ContactPhotoHelper'
]).init();
suite('Render contact form', function() {
suiteSetup(function() {
mocksForm.suiteSetup();
window.ExtServices = MockExtServices;
realFb = window.fb;
window.fb = Mockfb;
realThumbnailImage = utils.thumbnailImage;
utils.thumbnailImage = MockThumbnailImage;
document.body.innerHTML = MockFormDom;
footer = document.querySelector('footer');
subject = contacts.Form;
ActivityHandler = {
currentlyHandling: false
};
subject.init();
});
suiteTeardown(function() {
window.fb = realFb;
utils.thumbnailImage = realThumbnailImage;
navigator.mozL10n = realL10n;
mocksForm.suiteTeardown();
document.body.innerHTML = '';
});
setup(function() {
mockContact = new MockContactAllFields();
});
teardown(function() {
window.fb.setIsFbContact(false);
window.fb.setIsFbLinked(false);
});
function assertSaveState(value) {
var element = document.body.querySelector('#save-button');
assert.equal(element.getAttribute('disabled'), value);
}
function assertCarrierState(ele, value) {
var carrierField = ele.querySelector('input[data-field="carrier"]');
var state = carrierField.getAttribute('disabled');
assert.isTrue(value === state);
}
function assertAddDateState(value) {
assert.equal(document.getElementById('add-new-date').disabled, value);
}
suite('Render add form', function() {
test('without params', function() {
subject.render();
var toCheck = ['phone', 'address', 'email', 'note', 'date'];
for (var i = 0; i < toCheck.length; i++) {
var element = 'add-' + toCheck[i];
var cont = document.body.innerHTML;
assert.isTrue(cont.indexOf(element + '-0') > -1);
assertEmpty(element + '-0');
assert.isTrue(cont.indexOf(element + '-1') == -1);
assert.isTrue(footer.classList.contains('hide'));
if (toCheck[i] === 'date') {
// Check that the place holder 'date' appears
var spanEle = document.getElementById('date-text_0');
assert.equal(spanEle.getAttribute('data-l10n-id'),
'date-span-placeholder');
}
}
assertSaveState('disabled');
// The add date button shouldn't be disabled
assertAddDateState(false);
});
test('with tel params', function() {
var params = {
tel: '123'
};
subject.render(params);
var toCheck = ['phone', 'address', 'email', 'note'];
var cont = document.body.innerHTML;
for (var i = 0; i < toCheck.length; i++) {
var element = 'add-' + toCheck[i];
assert.isTrue(cont.indexOf(element + '-0') > -1);
assert.isTrue(cont.indexOf(element + '-1') == -1);
}
var value = document.querySelector('#number_0').value;
assert.equal(value, params.tel);
var valueEmail = document.querySelector('#email_0').value;
assert.isFalse(valueEmail === params.tel);
assert.equal(valueEmail, '');
assert.isTrue(footer.classList.contains('hide'));
assertSaveState(null);
});
test('with email params', function() {
var params = {
email: '123'
};
subject.render(params);
var toCheck = ['phone', 'address', 'email', 'note'];
var cont = document.body.innerHTML;
for (var i = 0; i < toCheck.length; i++) {
var element = 'add-' + toCheck[i];
assert.isTrue(cont.indexOf(element + '-0') > -1);
assert.isTrue(cont.indexOf(element + '-1') == -1);
}
var value = document.querySelector('#number_0').value;
var valueEmail = document.querySelector('#email_0').value;
assert.isTrue(valueEmail === params.email);
assert.equal(value, '');
assert.isTrue(footer.classList.contains('hide'));
assertSaveState(null);
});
test('with email and tel params', function() {
var params = {
tel: '234',
email: '123'
};
subject.render(params);
var toCheck = ['phone', 'address', 'email', 'note'];
var cont = document.body.innerHTML;
for (var i = 0; i < toCheck.length; i++) {
var element = 'add-' + toCheck[i];
assert.isTrue(cont.indexOf(element + '-0') > -1);
assert.isTrue(cont.indexOf(element + '-1') == -1);
}
var value = document.querySelector('#number_0').value;
assert.isTrue(value === params.tel);
var valueEmail = document.querySelector('#email_0').value;
assert.isTrue(valueEmail === params.email);
assert.isTrue(footer.classList.contains('hide'));
assertSaveState(null);
});
test('with date params', function() {
var params = {
date: new Date(0)
};
subject.render(params);
var valueDate = document.querySelector('#date_0').valueAsDate;
assert.equal(valueDate.toDateString(), params.date.toDateString());
assertSaveState(null);
});
test('Initially the carrier field must be in disabled state', function() {
subject.render();
var element = document.body.querySelector('#add-phone-0');
assertCarrierState(element, 'disabled');
});
test('If email is a blank string then done button must be disabled',
function() {
var params = {
email: ' '
};
subject.render(params);
assertSaveState('disabled');
}
);
test('If tel is filled and carrier empty done button must be enabled',
function() {
var params = {
tel: [{
carrier: '',
value: '123456'
}]
};
subject.render(params);
assertSaveState(null);
}
);
test('Empty form and label changes keeps the done button disabled',
function() {
subject.render();
var formView = document.getElementById('view-contact-form');
// Sending this custom event is equivalent to changing the select value
// at UI level (see contacts.js#handleSelectTagDone)
var valueModifiedEvent = new CustomEvent('ValueModified', {
bubbles: true,
detail: {
prevValue: 'work',
newValue: 'home'
}
});
formView.dispatchEvent(valueModifiedEvent);
assertSaveState('disabled');
});
test('Date tags are filtered properly', function() {
subject.render();
document.getElementById('add-new-date').click();
var date1 = document.getElementById('date_type_0');
assert.equal(date1.dataset.value, 'birthday');
var date2 = document.getElementById('date_type_1');
assert.equal(date2.dataset.value, 'anniversary');
});
test('Reset button phone field', function() {
var phoneNumberField = document.getElementById('number_0');
phoneNumberField.value = '123456';
phoneNumberField.nextElementSibling.click();
assert.isTrue(phoneNumberField.value === '');
});
});
requireApp('communications/contacts/services/contacts.js');
suite('Render edit form', function() {
function assertDateContent(selector, date) {
var inputDate = document.body.querySelector(selector).
querySelector('input[type="date"]');
var contentDate = inputDate.valueAsDate;
assert.equal(contentDate.toUTCString(), date.toUTCString());
assert.equal(inputDate.previousElementSibling.
textContent.trim(), MOCK_NUMERIC_DATE_STRING);
assert.isFalse(inputDate.previousElementSibling.
classList.contains('placeholder'));
}
var NO_OP = function() {};
var noteSelector = '[data-field="note"]';
test('no scroll on first load', function() {
subject.render(mockContact);
var container = document.getElementById('contact-form').parentNode;
assert.equal(container.scrollTop, 0);
});
test('no scroll memorised from previous renders', function() {
subject.render(mockContact);
var container = document.getElementById('contact-form').parentNode;
// scroll container
container.scrollTop = 100;
subject.render(mockContact);
assert.equal(container.scrollTop, 0);
});
test('with no name', function() {
mockContact.givenName.pop();
subject.render(mockContact);
var nameField = document.querySelector('#givenName');
assert.equal(nameField.value, '');
});
test('with no last name', function() {
mockContact.familyName.pop();
subject.render(mockContact);
var nameField = document.querySelector('#familyName');
assert.equal(nameField.value, '');
});
test('with all fields', function() {
// For this test we need a contact with the same number of items
// on the used fields (phone, address, email, note)
mockContact.tel.pop();
mockContact.email.pop();
subject.render(mockContact);
var cont = document.body.innerHTML;
var toCheck = ['phone', 'address', 'email', 'note', 'date'];
for (var i = 0; i < toCheck.length; i++) {
var element = 'add-' + toCheck[i];
assert.isTrue(cont.indexOf(element + '-0') > -1);
assert.isTrue(cont.indexOf(element + '-1') == -1);
}
assertPhoneData(0);
assertEmailData(0);
assert.isFalse(footer.classList.contains('hide'));
});
test('with only birthday', function() {
subject.render(mockContact);
var cont = document.body.innerHTML;
var element = 'add-date';
assert.isTrue(cont.indexOf(element + '-0') > -1);
assert.isTrue(cont.indexOf(element + '-1') === -1);
assertDateContent('#' + element + '-0', mockContact.bday);
// The add date button shouldn't be disabled
assertAddDateState(false);
});
test('with birthday and anniversary', function() {
mockContact.anniversary = new Date(Date.UTC(1970,0,1));
subject.render(mockContact);
var cont = document.body.innerHTML;
var element = 'add-date';
assert.isTrue(cont.indexOf(element + '-0') > -1);
assert.isTrue(cont.indexOf(element + '-1') > -1);
assert.isTrue(cont.indexOf(element + '-2') === -1);
assertDateContent('#' + element + '-0', mockContact.bday);
assertDateContent('#' + element + '-1', mockContact.anniversary);
// The add date button should be disabled
assertAddDateState(true);
});
test('Birthday first day of the year is rendered properly', function() {
teardown(() => {
MOCK_NUMERIC_DATE_STRING = REAL_MOCK_NUMERIC_DATE_STRING;
});
mockContact.bday = new Date(2014, 0, 1);
subject.render(mockContact);
var element = 'add-date';
var REAL_MOCK_NUMERIC_DATE_STRING = MOCK_NUMERIC_DATE_STRING;
MOCK_NUMERIC_DATE_STRING =
new Date(Date.UTC(2014, 0, 1)).toLocaleString(navigator.languages, {
year: 'numeric',
month: 'numeric',
day: 'numeric'
}
);
assertDateContent('#' + element + '-0', mockContact.bday);
});
test('Dates are saved preserving their timestasmp referred to UTC',
function() {
var deviceContact = new MockContactAllFields();
subject.render(deviceContact);
subject.saveContact();
assert.equal(deviceContact.bday.getTime(), 0);
});
test('if the tel field is null, is ignored',
function(done) {
var calls = 0;
var target = document.getElementById('throbber');
var config = { attributes: true };
// we need to check for the change after the contact is saved,
// so we wait for the second time the throbber is modified
// (first shown, then hidden)
var observer = new MutationObserver(function(mutations) {
if (++calls === 2) {
observer.disconnect();
assert.equal(deviceContact.tel.length, 1);
done();
}
});
observer.observe(target, config);
var deviceContact = new MockContactAllFields();
deviceContact.tel[0].value = null;
subject.render(deviceContact);
assert.equal(deviceContact.tel.length, 2);
subject.saveContact();
});
test('if the email field is null, is ignored',
function(done) {
var calls = 0;
var target = document.getElementById('throbber');
var config = { attributes: true };
// same as before, we check for the change after the contact is saved,
// so we wait for the second time the throbber is modified
// (first shown, then hidden)
var observer = new MutationObserver(function(mutations) {
if (++calls === 2) {
observer.disconnect();
assert.equal(deviceContact.email.length, 1);
done();
}
});
observer.observe(target, config);
var deviceContact = new MockContactAllFields();
deviceContact.email[0].value = null;
subject.render(deviceContact);
assert.equal(deviceContact.email.length, 2);
subject.saveContact();
});
test('if the address field is null, is ignored',
function() {
var deviceContact = new MockContactAllFields();
deviceContact.adr.unshift({'type': ['personal']});
subject.render(deviceContact);
assert.equal(deviceContact.adr.length, 2);
subject.saveContact();
assert.equal(deviceContact.adr.length, 1);
});
test('if tel field has a value, carrier input must be in regular state',
function() {
subject.render(mockContact);
var element = document.body.querySelector('#add-phone-0');
assertCarrierState(element, null);
});
test('if tel field has no value, there is no visible telephone field',
function() {
mockContact.tel = [];
subject.render(mockContact);
var element = document.body.querySelector('#add-phone-0');
assert.isTrue(element === null);
});
test('Removing a note preserves the others', function() {
mockContact.note.push('other note');
subject.render(mockContact);
// Three as we need to count the template one
assert.equal(document.querySelectorAll(noteSelector).length, 3);
var firstNoteContainer = document.getElementById('add-note-0');
var delButton = firstNoteContainer.querySelector(
'button.img-delete-button');
delButton.onclick({
eventX: 100,
eventY: 100,
target: delButton,
preventDefault: NO_OP
});
// Two as we need to count the template one
assert.equal(document.querySelectorAll(noteSelector).length, 2);
});
test('Removing all notes collapses the notes section', function() {
mockContact.note.push('other note');
subject.render(mockContact);
// Three as we need to count the template one
assert.equal(document.querySelectorAll(noteSelector).length, 3);
for(var j = 0; j < 2; j++) {
var noteContainer = document.getElementById('add-note-' + j);
var delButton = noteContainer.querySelector(
'button.img-delete-button');
var synthEvent = {
eventX: 100,
eventY: 100,
target: delButton,
preventDefault: NO_OP
};
delButton.onclick(synthEvent);
}
// Only the template remains
var presentNotes = document.querySelectorAll(
'.note-template[data-template]');
assert.equal(presentNotes.length, 1);
assert.isTrue(presentNotes.item(0).id.indexOf('#') !== -1);
});
test('FB Contact. e-mail, phone and photo from Facebook', function() {
window.fb.setIsFbContact(true);
var deviceContact = new MockContactAllFields();
var fbContact = new Mockfb.Contact(deviceContact);
fbContact.getDataAndValues().onsuccess = function() {
deviceContact.photo = null;
subject.render(deviceContact, null, this.result);
var cont = document.body.innerHTML;
var toCheck = ['phone', 'email'];
for (var i = 0; i < toCheck.length; i++) {
var element = 'add-' + toCheck[i];
assert.isTrue(cont.indexOf(element + '-0') > -1);
assert.isTrue(cont.indexOf(element + '-1') > -1);
assert.isTrue(cont.indexOf(element + '-2') == -1);
var domElement0 = document.querySelector('#' + element + '-' + '0');
assert.isTrue(domElement0.classList.contains('facebook'));
assert.isTrue(domElement0.querySelector('.icon-delete') === null);
}
assertPhoneData(0);
assertEmailData(0);
assert.isFalse(footer.classList.contains('hide'));
// Remove Field icon photo should not be present
var thumbnail = document.querySelector('#thumbnail-action');
assert.isTrue(thumbnail.classList.contains('facebook'));
};
});
test('FB Contact. Address from Facebook', function() {
window.fb.setIsFbContact(true);
var fbContact = new Mockfb.Contact(mockContact);
fbContact.getDataAndValues().onsuccess = function() {
subject.render(mockContact, null, this.result);
var content = document.body.innerHTML;
var toCheck = ['address'];
for (var i = 0; i < toCheck.length; i++) {
var element = 'add-' + toCheck[i];
assert.isTrue(content.indexOf(element + '-0') > -1);
assert.isTrue(content.indexOf(element + '-1') > -1);
assert.isTrue(content.indexOf(element + '-2') === -1);
var domElement0 = document.querySelector('#' + element + '-' + '0');
assert.isTrue(domElement0.classList.contains('facebook'),
'Class Removed and Facebook present');
assert.isTrue(domElement0.querySelector('.icon-delete') === null,
'Icon delete not present');
}
assertAddressData(0, this.result[0]);
assert.isFalse(footer.classList.contains('hide'));
};
});
test('FB Contact. organization from Facebook', function() {
window.fb.setIsFbContact(true);
var deviceContact = new MockContactAllFields();
var fbContact = new Mockfb.Contact(deviceContact);
fbContact.getDataAndValues().onsuccess = function() {
subject.render(deviceContact, null, this.result);
var org = document.querySelector('input[name="org"]');
assert.isTrue(org.parentNode.classList.contains('facebook'));
};
});
test('FB Contact. Birthday from Facebook', function() {
window.fb.setIsFbContact(true);
var fbContact = new Mockfb.Contact(mockContact);
fbContact.getDataAndValues().onsuccess = function() {
subject.render(mockContact, null, this.result);
var content = document.body.innerHTML;
var element = 'add-date';
assert.isTrue(content.indexOf(element + '-0') > -1);
assert.isTrue(content.indexOf(element + '-1') === -1);
assertDateContent('#' + element + '-0', mockContact.bday);
var domElement0 = document.querySelector('#' + element + '-' + '0');
assert.isTrue(domElement0.classList.contains('facebook'),
'Class Removed and Facebook present');
assert.isTrue(domElement0.querySelector('.icon-delete') === null,
'Icon delete not present');
// The add date button shouldn't be disabled
assertAddDateState(false);
assert.isFalse(footer.classList.contains('hide'));
};
});
test('FB Contact. Linking and promoting given name', function() {
window.fb.setIsFbContact(true);
window.fb.setIsFbLinked(false);
var promoteToLinkedSpy = sinon.spy(Mockfb, 'promoteToLinked');
var setPropagatedFlagSpy = sinon.spy(window.fb, 'setPropagatedFlag');
mockContact.givenName.pop();
var fbContact = new Mockfb.Contact(mockContact);
fbContact.getDataAndValues().onsuccess = function() {
subject.render(mockContact, null, this.result);
document.querySelector('#givenName').value = '';
subject.saveContact();
assert.isTrue(promoteToLinkedSpy.called);
assert.isTrue(setPropagatedFlagSpy.calledWithMatch('givenName'));
promoteToLinkedSpy.restore();
setPropagatedFlagSpy.restore();
};
});
test('FB Contact. Linking and promoting family name', function() {
window.fb.setIsFbContact(true);
window.fb.setIsFbLinked(false);
var promoteToLinkedSpy = sinon.spy(Mockfb, 'promoteToLinked');
var setPropagatedFlagSpy = sinon.spy(window.fb, 'setPropagatedFlag');
mockContact.familyName.pop();
var fbContact = new Mockfb.Contact(mockContact);
fbContact.getDataAndValues().onsuccess = function() {
subject.render(mockContact, null, this.result);
document.querySelector('#familyName').value = '';
subject.saveContact();
assert.isTrue(promoteToLinkedSpy.called);
assert.isTrue(setPropagatedFlagSpy.calledWithMatch('familyName'));
promoteToLinkedSpy.restore();
setPropagatedFlagSpy.restore();
};
});
test('FB Linked. e-mail and phone both from FB and device', function() {
window.fb.setIsFbContact(true);
window.fb.setIsFbLinked(true);
var fbContact = new Mockfb.Contact(mockContact);
fbContact.getDataAndValues().onsuccess = function() {
this.result[0].tel[1] = {
'value': '+34616885989',
'type': ['Mobile'],
'carrier': 'NTT'
};
this.result[0].email[1] = {
'type': ['work'],
'value': 'workwithme@tid.es'
};
subject.render(mockContact, null, this.result);
var cont = document.body.innerHTML;
var toCheck = ['phone', 'email'];
for (var i = 0; i < toCheck.length; i++) {
var element = 'add-' + toCheck[i];
assert.isTrue(cont.indexOf(element + '-0') > -1);
assert.isTrue(cont.indexOf(element + '-1') > -1);
var domElement0 = document.querySelector('#' + element + '-' + '0');
assert.isTrue(domElement0.classList.contains('facebook'));
assert.isTrue(domElement0.querySelector('.icon-delete') === null);
var domElement1 = document.querySelector('#' + element + '-' + '1');
assert.isFalse(domElement1.classList.contains('facebook'));
}
for (var c = 0; c < 2; c++) {
assertPhoneData(c, this.result[0]);
assertEmailData(c, this.result[0]);
}
};
});
test('FB Linked. Photo local to the device', function() {
window.fb.setIsFbContact(true);
window.fb.setIsFbLinked(true);
var fbContact = new Mockfb.Contact(mockContact);
fbContact.getDataAndValues().onsuccess = function() {
subject.render(mockContact, null, this.result);
var thumbnail = document.querySelector('#thumbnail-action');
assert.isFalse(thumbnail.classList.contains('facebook'));
};
});
test('FB Linked. Birthday local to the device', function() {
window.fb.setIsFbContact(true);
window.fb.setIsFbLinked(true);
var fbContact = new Mockfb.Contact(mockContact);
fbContact.getDataAndValues().onsuccess = function() {
delete this.result[1][new Date(0).toString()];
subject.render(mockContact, null, this.result);
assertDateContent('#add-date-0', mockContact.bday);
var domElement0 = document.querySelector('#add-date-0');
assert.isFalse(domElement0.classList.contains('facebook'),
'Class Removed or Facebook present');
// The add date button shouldn't be disabled
assertAddDateState(false);
};
});
});
suite('Generate full contact name', function() {
setup(function() {
// Bypass the contacts matcher when saving contact
LazyLoader.load(['/shared/js/simple_phone_matcher.js',
'/shared/js/contacts/contacts_matcher.js'], function() {
Matcher.match = function() {};
});
});
test('given name is empty', function() {
var deviceContact = new MockContactAllFields();
deviceContact.givenName = null;
deviceContact.name = null;
subject.render(deviceContact);
subject.saveContact();
assert.equal(deviceContact.name[0], deviceContact.familyName);
});
test('family name is empty', function() {
var deviceContact = new MockContactAllFields();
deviceContact.familyName = null;
deviceContact.name = null;
subject.render(deviceContact);
subject.saveContact();
assert.equal(deviceContact.name[0], deviceContact.givenName);
});
test('both name fields are empty', function() {
var deviceContact = new MockContactAllFields();
deviceContact.givenName = null;
deviceContact.familyName = null;
deviceContact.name = null;
subject.render(deviceContact);
subject.saveContact();
assert.equal(deviceContact.name[0], undefined);
});
test('both name fields are present', function() {
var deviceContact = new MockContactAllFields();
deviceContact.name = null;
subject.render(deviceContact);
subject.saveContact();
assert.equal(deviceContact.name[0],
deviceContact.givenName + ' ' + deviceContact.familyName);
});
});
suite('Delete Contact', function() {
var deleteButton;
var realSearch;
var realMozContacts;
setup(function() {
subject.render(mockContact);
});
suiteSetup(function() {
deleteButton = document.querySelector('#delete-contact');
realSearch = window.Search;
window.Search = MockContactsSearch;
realMozContacts = navigator.mozContacts;
navigator.mozContacts = new MockMozContactsObj([]);
});
suiteTeardown(function() {
window.Search = realSearch;
navigator.mozContacts = realMozContacts;
});
test('show confirm', function() {
deleteButton.click();
assert.isTrue(ConfirmDialog.showing);
assert.equal(ConfirmDialog.text, 'deleteConfirmMsg');
ConfirmDialog.hide();
});
test('cancel delete', function() {
deleteButton.click();
ConfirmDialog.executeNo();
assert.isFalse(ConfirmDialog.showing);
});
test('delete contact while in search mode', function(done) {
deleteButton.click();
sinon.stub(Search,
'isInSearchMode', function() {
return true;
});
var exitSearchModeStub = sinon.stub(Search,
'exitSearchMode', function() {
assert.isTrue(true);
contactsStub.restore();
exitSearchModeStub.restore();
done();
});
var contactsStub = sinon.stub(window.navigator.mozContacts,
'remove', function() {
return {
set onsuccess(cb) {
cb();
}
};
});
ConfirmDialog.executeYes();
});
});
suite('utils.mis.formatDate', () => {
test('defaultFormat', () => {
const MOCK_DATE_STRING =
new Date(Date.UTC(1970, 0, 1)).toLocaleString(navigator.languages, {
year: 'numeric',
month: 'short',
day: 'numeric'
}
);
assert.equal(utils.misc.formatDate(mockContact.bday), MOCK_DATE_STRING);
});
test('customFormat', () => {
assert.equal(utils.misc.formatDate(mockContact.bday, {
year: 'numeric',
month: 'numeric',
day: 'numeric'
}), MOCK_NUMERIC_DATE_STRING);
});
});
function assertEmpty(id) {
var fields = document.querySelectorAll('#' + id + ' input');
for (var i = 0; i < fields.length; i++) {
var currField = fields[i];
if (currField.dataset.field && currField.dataset.field != 'type') {
assert.isTrue(fields[i].value === '');
}
}
}
function assertPhoneData(c, phoneData) {
var data = phoneData || mockContact;
var valuePhone = document.querySelector('#number_' + c).value;
var typePhone = document.querySelector('#tel_type_' + c).textContent.trim();
var carrierPhone = document.querySelector('#carrier_' + c).value;
assert.isTrue(valuePhone === data.tel[c].value);
assert.isTrue(typePhone === data.tel[c].type[0]);
assert.isTrue(carrierPhone === data.tel[c].carrier);
}
function assertEmailData(c, emailData) {
var data = emailData || mockContact;
var valueEmail = document.querySelector('#email_' + c).value;
var typeEmail = document.querySelector('#email_type_' + c).textContent;
assert.isTrue(valueEmail === data.email[c].value);
assert.isTrue(typeEmail === data.email[c].type[0]);
}
function assertAddressData(c, addrData) {
var data = addrData || mockContact;
var valueType = document.querySelector('#address_type_' + c).textContent;
assert.isTrue(valueType === data.adr[c].type[0],
'Type Value as Expected');
valueType = document.querySelector('#locality_' + c).value;
assert.isTrue(valueType === data.adr[c].locality,
'Type Value as Expected');
valueType = document.querySelector('#countryName_' + c).value;
assert.isTrue(valueType === data.adr[c].countryName,
'Type Value as Expected');
}
});

View file

@ -0,0 +1,420 @@
'use strict';
/* global loadBodyHTML */
/* global MocksHelper */
/* global contacts */
/* global asyncStorage */
/* global MockContactsListObj */
/* global ContactsService */
/* global ConfirmDialog */
/* global ICEData, MockContactsSettings, ICE */
requireApp('communications/contacts/test/unit/mock_header_ui.js');
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
requireApp('communications/contacts/test/unit/mock_asyncstorage.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
requireApp('communications/contacts/test/unit/mock_cache.js');
requireApp('communications/contacts/js/utilities/ice_data.js');
requireApp('communications/contacts/js/views/ice_settings.js');
requireApp('communications/contacts/test/unit/mock_contacts_list_obj.js');
requireApp('communications/contacts/test/unit/mock_contacts_settings.js');
require('/shared/js/component_utils.js');
require('/shared/elements/gaia_switch/script.js');
require('/shared/test/unit/mocks/mock_confirm_dialog.js');
require('/shared/test/unit/mocks/mock_ice_store.js');
var mocksHelper = new MocksHelper([
'asyncStorage',
'Cache',
'ConfirmDialog',
'Contacts',
'ICEStore',
'HeaderUI'
]);
mocksHelper.init();
suite('ICE Settings view', function() {
var subject;
var realContactsList;
var realContactsSettings;
var defaultLabel = 'ICESelectContact';
var cid1 = '1', cid2 = '2', fbcid3 = '3';
suiteSetup(function() {
mocksHelper.suiteSetup();
subject = ICE;
realContactsSettings = contacts.Settings;
contacts.Settings = MockContactsSettings;
realContactsList = contacts.List;
contacts.List = MockContactsListObj;
});
suiteTeardown(function() {
contacts.List = realContactsList;
contacts.Settings = realContactsSettings;
});
setup(function() {
setupHTML();
this.sinon.stub(
ContactsService,
'get',
function(id, successCB, errorCB) {
if (!id) {
successCB();
return;
}
var contacts = [];
contacts.push({
id: cid1,
givenName: ['John'],
familyName: ['Doe']
});
contacts.push({
id: cid2,
givenName: ['Albert'],
familyName: ['Pla']
});
contacts.push({
id: fbcid3,
givenName: ['Cristian'],
familyName: ['Martin'],
isFB: true
});
var contact = contacts[id - 1];
successCB(contact, contact.isFB);
}
);
});
teardown(function() {
subject.reset();
window.asyncStorage.clear();
ContactsService.get.restore();
});
function setupHTML() {
loadBodyHTML('/contacts/elements/settings.html');
// We loaded a template, expand it
var template = document.getElementsByTagName('template')[0].innerHTML;
var section = document.createElement('section');
section.id = 'settings-wrapper';
section.innerHTML = template;
document.body.innerHTML = '';
document.body.appendChild(section);
}
function assertIceContacts(iceStates) {
var ice1 = document.getElementById('select-ice-contact-1');
assert.equal(ice1.dataset.contactId, iceStates[0].contactId);
if (iceStates[0].label) {
assert.equal(ice1.textContent.trim(), iceStates[0].label);
}
else {
assert.equal(ice1.dataset.l10nId, defaultLabel);
}
var ice2 = document.getElementById('select-ice-contact-2');
assert.equal(ice2.dataset.contactId, iceStates[1].contactId);
if (iceStates[1].label) {
assert.equal(ice2.textContent.trim(), iceStates[1].label);
}
else {
assert.equal(ice2.dataset.l10nId, defaultLabel);
}
assert.equal(ice1.disabled, !iceStates[0].active);
assert.equal(ice2.disabled, !iceStates[1].active);
var iceCheck1 = document.querySelector('[name="ice-contact-1-enabled"]');
var iceCheck2 = document.querySelector('[name="ice-contact-2-enabled"]');
assert.ok(!iceCheck1.disabled);
assert.ok(!iceCheck2.disabled);
}
suite('> Initialization', function() {
setup(function() {
this.sinon.spy(asyncStorage, 'getItem');
});
test('> No ice contacts', function(done) {
window.asyncStorage.keys = {
'ice-contacts': []
};
subject.refresh(function() {
// On init and when we do the listening
sinon.assert.calledOnce(asyncStorage.getItem);
assertIceContacts([{ contactId: '', active: false},
{ contactId: '', active: false}]);
done();
});
});
test('> With 1 contact enabled. ICE Contact 1', function(done) {
window.asyncStorage.keys = {
'ice-contacts': [
{
id: cid1,
active: true
}
]
};
subject.refresh(function() {
sinon.assert.calledTwice(ContactsService.get);
assertIceContacts([{ contactId: cid1, label: 'John Doe', active: true},
{ contactId: '', active: false}]);
done();
});
});
test('> With 1 contact enabled. ICE Contact 2', function(done) {
window.asyncStorage.keys = {
'ice-contacts': [
{},
{
id: cid2,
active: true
}
]
};
subject.refresh(function() {
sinon.assert.calledTwice(ContactsService.get);
assertIceContacts([{ contactId: '', active: false},
{ contactId: cid2, label: 'Albert Pla', active: true}]);
done();
});
});
test('> With 1 contact enabled. No name. Only has tel number',
function(done) {
window.asyncStorage.keys = {
'ice-contacts': [
{
id: cid1,
active: true
}
]
};
var targetTelNumber = '678987654';
ContactsService.get.restore();
this.sinon.stub(
ContactsService,
'get',
function(id, successCB, errorCB) {
if (!id) {
successCB();
return;
}
var contacts = [];
contacts.push({
id: cid1,
givenName: [],
familyName: null,
tel: [
{
type: ['other'],
value: targetTelNumber
}
]
});
var contact = contacts[id - 1];
successCB(contact);
}
);
subject.refresh(function() {
sinon.assert.calledTwice(ContactsService.get);
assertIceContacts([{
label: targetTelNumber, contactId: cid1, active: true
},{
contactId: '', active: false
}]);
done();
});
});
test('> With ICE contact 1 disabled', function(done) {
window.asyncStorage.keys = {
'ice-contacts': [
{
id: null,
active: false
}
]
};
subject.refresh(function() {
sinon.assert.calledTwice(ContactsService.get);
assertIceContacts([{ contactId: '', active: false},
{ contactId: '', active: false}]);
done();
});
});
test('> With ICE contact 2 disabled', function(done) {
window.asyncStorage.keys = {
'ice-contacts': [
{},
{
id: null,
active: false
}
]
};
subject.refresh(function() {
sinon.assert.calledTwice(ContactsService.get);
assertIceContacts([{ contactId: '', active: false},
{ contactId: '', active: false}]);
done();
});
});
test('> With 2 contacts enabled', function(done) {
window.asyncStorage.keys = {
'ice-contacts': [
{
id: cid1,
active: true
}, {
id: cid2,
active: true
}
]
};
subject.refresh(function() {
sinon.assert.calledTwice(ContactsService.get);
assertIceContacts([{ contactId: cid1, label: 'John Doe', active: true},
{ contactId: cid2, label: 'Albert Pla', active: true}]);
done();
});
});
});
suite('> Modify ICE contacts', function() {
suiteSetup(function() {
sinon.spy(ICEData, 'setICEContact');
});
suiteTeardown(function() {
ICEData.setICEContact.restore();
});
setup(function() {
window.asyncStorage.keys = {
'ice-contacts': [
{
id: cid1,
active: true
}, {
id: cid2,
active: true
}
]
};
});
test('> change state saves ICE Datastore', function(done) {
subject.refresh(function() {
var switch1 = document.querySelector('[name=ice-contact-1-enabled]');
// Disable 1
switch1.click();
sinon.assert.calledOnce(ICEData.setICEContact);
sinon.assert.calledWith(ICEData.setICEContact, null, 0, false);
done();
});
});
test('> remove ICE Contact 1 and ICE Contact 2 remains', function(done) {
subject.refresh(function() {
assertIceContacts([{ contactId: cid1, label: 'John Doe', active: true},
{ contactId: cid2, label: 'Albert Pla', active: true}]);
ICEData.removeICEContact(cid1).then(function() {
subject.refresh(function() {
assertIceContacts([{ contactId: '', active: false},
{ contactId: cid2, label: 'Albert Pla', active: true}]);
done();
});
});
});
});
});
suite('> Error handling ', function() {
var handleClick;
setup(function() {
this.sinon.stub(contacts.List, 'handleClick', function(cb) {
handleClick = cb;
});
window.asyncStorage.keys = {
'ice-contacts': [
{
id: cid1,
active: true
}
]
};
});
teardown(function() {
handleClick = null;
});
function clickOnList(id) {
document.getElementById('select-ice-contact-1').click();
handleClick(id);
}
function assertErrorMessage(code, expectedCode, cb) {
assert.equal(code, expectedCode);
ConfirmDialog.show.restore();
cb();
}
test(' repeated contact', function(done) {
subject.refresh(function() {
clickOnList(cid1);
sinon.stub(ConfirmDialog, 'show', function(param1, code) {
assertErrorMessage(code, 'ICERepeatedContact', done);
});
});
});
});
});

View file

@ -0,0 +1,142 @@
/* global MockImageLoader */
/* global MocksHelper */
/* global loadBodyHTML */
/* global ICEData */
/* global ICEView */
'use strict';
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/js/contacts/utilities/ice_store.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
requireApp('communications/contacts/test/unit/mock_utils.js');
requireApp('communications/contacts/test/unit/mock_ice_data.js');
var mocksForICe = new MocksHelper([
'Contacts', 'ICEData', 'LazyLoader'
]).init();
suite('ICE contacts view', function() {
mocksForICe.attachTestHelpers();
function dummyRowBuilder(id, node) {
return node.cloneNode(true);
}
var realImageLoader;
var fakeContacts = [
{
id: 1,
displayName: 'Mark Botellas'
},
{
id: 2,
displayName: 'Tom Wislow'
},
{
id: 3,
displayName: 'Alex Kid'
}
];
suiteSetup(function(done) {
realImageLoader = window.ImageLoader;
window.ImageLoader = MockImageLoader;
requireApp('communications/contacts/js/views/ice.js', done);
});
setup(function() {
cleanHTML();
});
suiteTeardown(function() {
window.ImageLoader = realImageLoader;
});
function cleanHTML() {
loadBodyHTML('/contacts/elements/ice.html');
// We loaded a template, expand it and leave the desired dom
var template = document.getElementsByTagName('template')[0].innerHTML;
var section = document.createElement('section');
section.id = 'ice-view';
section.innerHTML = template;
document.body.innerHTML = '';
document.body.appendChild(section);
}
// Helper function to create fake contacts list
// elements to be used in the ice list.
function createContactsListRows(contacts) {
var section = document.createElement('section');
section.id = 'contacts-list';
contacts.forEach(function(contact) {
var li = document.createElement('li');
li.dataset.uuid = contact.id;
li.innerHTML = '<p>' + contact.displayName + '</p>';
section.appendChild(li);
});
document.body.appendChild(section);
}
function defaultContacts() {
createContactsListRows(fakeContacts);
}
suite('> initialization', function() {
setup(function() {
defaultContacts();
});
test('> init with ids', function() {
ICEView.init([1,2], dummyRowBuilder);
var ice1 = document.querySelector('#ice-list [data-uuid="1"]');
var ice2 = document.querySelector('#ice-list [data-uuid="2"]');
assert.isNotNull(ice1);
assert.isNotNull(ice2);
});
test('> init with invalid ice contacts', function() {
ICEView.init([1,2], dummyRowBuilder);
var ice3 = document.querySelector('#ice-list [data-uuid="3"]');
assert.isNull(ice3);
});
});
suite('> Contacts changed', function() {
setup(function() {
defaultContacts();
ICEView.init([1,2], dummyRowBuilder);
this.clock = sinon.useFakeTimers();
});
teardown(function() {
this.clock.restore();
});
test('> ICE contact changed', function() {
// Change an element in the 'list', and trigger custom
// contact change
var ice1 = document.querySelector('#ice-list [data-uuid="1"]');
assert.isTrue(ice1.innerHTML.indexOf(fakeContacts[0].displayName) !== -1);
// Change the contact
var contact1 = document.querySelector('#contacts-list [data-uuid="1"]');
contact1.firstChild.textContent = 'Peter Pan';
// Simulate a contact change
ICEData.iceContacts = [ {
id: 1,
active: true
}];
ICEData._mTriggerChange();
this.clock.tick(500);
ice1 = document.querySelector('#ice-list [data-uuid="1"]');
assert.equal(contact1.innerHTML, ice1.innerHTML);
});
});
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,86 @@
/* global LazyLoader, MatchingUI */
'use strict';
require('/shared/js/lazy_loader.js');
requireApp('communications/contacts/views/matching/js/boot.js');
suite('Matching Boot', function() {
var realMatchingUI;
setup(function() {
realMatchingUI = window.MatchingUI;
window.MatchingUI = {
init: function foo() {}
};
});
teardown(function() {
window.MatchingUI = realMatchingUI;
realMatchingUI = null;
});
suite('DOMContentLoaded', function() {
var stub;
setup(function() {
stub = this.sinon.stub(LazyLoader, 'load', function(files) {
return {
then: function(cb) {
cb();
}
};
});
window.dispatchEvent(new CustomEvent('DOMContentLoaded'));
});
teardown(function() {
LazyLoader.load.restore();
});
test(' > LazyLoader must be called once', function() {
// We must have 2 calls to LazyLoader
assert.isTrue(stub.calledOnce);
});
test(' > Call must ensure localization', function() {
// First of all we need to bring l10n library for localization
var call = stub.getCall(0);
assert.isTrue(Array.isArray(call.args[0]));
assert.equal(call.args[0][0], '/shared/js/l10n.js');
});
});
suite('Loaded', function() {
var realMozSetMessage;
suiteSetup(function() {
realMozSetMessage = navigator.mozSetMessageHandler;
navigator.mozSetMessageHandler = function foo() {};
});
suiteTeardown(function() {
navigator.mozSetMessageHandler = realMozSetMessage;
realMozSetMessage = null;
});
setup(function() {
this.sinon.stub(LazyLoader, 'load', function(files) {
return {
then: function(cb) {
cb();
}
};
});
});
test(' > UI is initialized', function() {
var MatchingUISpy = this.sinon.spy(MatchingUI, 'init');
window.dispatchEvent(new CustomEvent('load'));
assert.isTrue(MatchingUISpy.calledOnce);
});
});
});

View file

@ -0,0 +1,136 @@
'use strict';
/* global matchingDetailsData */
/* global MockMozL10n */
/* global MockCurtain */
/* global MockContactsService */
/* global LazyLoader */
/* global Curtain */
/* global Matcher */
/* global ContactsService */
/* global MergeHelper */
require('/shared/js/lazy_loader.js');
requireApp('communications/contacts/test/unit/mock_l10n.js');
requireApp('communications/contacts/test/unit/mock_matching_contacts.js.html');
requireApp('communications/contacts/test/unit/' +
'contacts_matching_ui_test_data.js');
requireApp('communications/contacts/test/unit/mock_contacts_service.js');
requireApp('communications/facebook/test/unit/mock_curtain.js');
requireApp('communications/contacts/views/matching/js/matching_controller.js');
suite('MatchingController', function() {
var realL10n, realCurtain, realContactsService, realMatcher, realMergeHelper,
realParentContactsService;
suiteSetup(function() {
realL10n = window.navigator.mozL10n;
window.navigator.mozL10n = MockMozL10n;
realCurtain = window.Curtain;
window.Curtain = MockCurtain;
realContactsService = window.ContactsService;
window.ContactsService = MockContactsService;
realMatcher = window.Matcher;
window.Matcher = {
match: function foo(){}
};
realMergeHelper = window.MergeHelper;
window.MergeHelper = {
merge: function foo() {
return {
then: function boo(cb) {
cb();
}
};
}
};
sinon.stub(LazyLoader, 'load', function(files, cb) {
cb();
});
realParentContactsService = parent.ContactsService;
parent.ContactsService = window.ContactsService;
});
suiteTeardown(function() {
window.navigator.mozL10n = realL10n;
realL10n = null;
window.Curtain = realCurtain;
realCurtain = null;
window.ContactsService = realContactsService;
realContactsService = null;
window.Matcher = realMatcher;
realMatcher = null;
window.MergeHelper = realMergeHelper;
realMergeHelper = null;
parent.ContactsService = realParentContactsService;
});
suite(' > On merge', function() {
var showSpy, hideMenuSpy, mergeSpy, hideSpy;
setup(function() {
showSpy = this.sinon.spy(Curtain, 'show');
hideMenuSpy = this.sinon.spy(Curtain, 'hideMenu');
hideSpy = this.sinon.spy(Curtain, 'hide');
mergeSpy = this.sinon.spy(MergeHelper, 'merge');
});
test('On merge event received', function() {
window.dispatchEvent(new CustomEvent('merge', {
'detail': {
'checkedContacts': {'1a': '1a'}
}
}));
assert.isTrue(showSpy.calledOnce);
assert.isTrue(hideMenuSpy.calledOnce);
assert.isTrue(mergeSpy.calledOnce);
assert.isTrue(hideSpy.calledOnce);
});
});
suite(' > On start', function() {
var showSpy, matchStub, getContactStub;
setup(function() {
showSpy = this.sinon.spy(Curtain, 'show');
getContactStub = this.sinon.stub(ContactsService, 'get',
function(contactID, successCb, errorCb) {
successCb(matchingDetailsData.user_id_1);
});
matchStub = this.sinon.stub(Matcher, 'match',
function(contact, type, callbacks) {
callbacks.onmatch();
});
});
test('Start method should call match', function(done) {
window.addEventListener('initUI', function fn() {
window.removeEventListener('initUI', fn);
done();
});
window.postMessage({
type: 'sync'
}, location.origin);
setTimeout(function() {
assert.isTrue(showSpy.calledOnce);
assert.isTrue(getContactStub.calledOnce);
assert.isTrue(matchStub.calledOnce);
// Add the extra time we add in matching_controller.js
// in the start function
}, 2000);
});
});
});

View file

@ -0,0 +1,198 @@
'use strict';
/* global CORRECT_MATCHED_VALUE */
/* global dataImage */
/* global dupContacts */
/* global MockMatchingContactsHtml */
/* global MocksHelper */
/* global MockMozL10n */
/* global MatchingUI */
/* eslint-disable */
require('/shared/js/lazy_loader.js');
require('/shared/js/sanitizer.js');
require('/shared/test/unit/mocks/mock_contact_photo_helper.js');
requireApp('communications/contacts/test/unit/mock_matching_contacts.js.html');
requireApp('communications/contacts/test/unit/mock_l10n.js');
requireApp('communications/contacts/test/unit/mock_utils.js');
requireApp('communications/contacts/test/unit/' +
'contacts_matching_ui_test_data.js');
requireApp('communications/contacts/views/matching/js/matching_ui.js');
var mocksHelperForContactMatchingUI = new MocksHelper([
'ContactPhotoHelper',
'ImageLoader',
'URL'
]).init();
suite('MatchingUI', function() {
mocksHelperForContactMatchingUI.attachTestHelpers();
var wrapper = null,
list, mergeAction, realL10n;
var masterContact = {
givenName: ['Manolo<>'],
familyName: ['García']
};
function checkItem(item, contact) {
var bdiNodes = item.querySelectorAll('bdi');
// Given and family names are displayed fine
assert.equal(
bdiNodes[0].textContent,
contact.givenName[0] + ' ' + contact.familyName[0]
);
// The second bdi node is the main reason for matching
// The current preference is tel, email and name
// Check in selectMainReason() of contacts_matching_ui.js
assert.equal(bdiNodes[1].textContent, CORRECT_MATCHED_VALUE);
// The image is ready for the image loader
assert.isTrue(item.querySelector('img').dataset.src == dataImage);
// At the beginning the contact is checked by default
assert.isTrue(item.querySelector('input[type="checkbox"]').checked);
}
suiteSetup(function() {
realL10n = window.navigator.mozL10n;
window.navigator.mozL10n = MockMozL10n;
});
suiteTeardown(function() {
window.navigator.mozL10n = realL10n;
});
function onUIReady(done) {
window.addEventListener('UIReady', function fn() {
window.removeEventListener('UIReady', fn);
done();
});
}
setup(function() {
wrapper = document.createElement('section');
wrapper.innerHTML = MockMatchingContactsHtml;
document.body.appendChild(wrapper);
list = wrapper.querySelector('#contacts-list-container ol');
mergeAction = wrapper.querySelector('#merge-action');
MatchingUI.init();
});
teardown(function() {
wrapper.remove();
});
test('Listens to the initUI event', function() {
// We can't always use dispatchEvent, because the listener is registered
// each time we call init. So we try it once here, and in the reste of the
// test we call directly the `load` method.
this.sinon.stub(MatchingUI, 'load');
window.dispatchEvent(
new CustomEvent('initUI', {
detail: {
type: 'matching',
contact: masterContact,
results: dupContacts
}
})
);
sinon.assert.calledWith(
MatchingUI.load,
'matching', masterContact, dupContacts
);
});
var runs = [
{
type: 'matching',
messageL10nId: 'suggestedDuplicatesMessage',
titleL10nId: null
}, {
type: 'listing',
messageL10nId: 'foundDuplicatesMessage',
titleL10nId: 'duplicatesFoundTitle'
}
];
runs.forEach(function(run) {
suite(run.type + ', ', function() {
setup(function(done) {
onUIReady(done);
MatchingUI.load(run.type, masterContact, dupContacts);
});
test('The UI is initialized correctly ', function() {
// The merge button is initially enabled because there are some
// duplicate contacts.
assert.isFalse(mergeAction.disabled);
var message = wrapper.querySelector('#duplicate-msg > p');
// The message is displayed correctly
var l10nAttrs = navigator.mozL10n.getAttributes(message);
assert.equal(l10nAttrs.args.name, 'Manolo&lt;&gt; García');
assert.equal(l10nAttrs.id, run.messageL10nId);
// The title is correct as well
var title = document.querySelector('title');
assert.equal(title.dataset.l10nId, run.titleL10nId);
// Duplicate contacts list is displayed correctly
var dupContactsKeys = Object.keys(dupContacts);
console.log(list.innerHTML);
assert.equal(list.children.length, dupContactsKeys.length);
dupContactsKeys.forEach(function(id) {
checkItem(list.querySelector('li[data-uuid="' + id + '"]'),
dupContacts[id].matchingContact);
});
});
test('Users are able to un-select and select contacts ', function(done) {
var firstItem = list.querySelector('li[data-uuid="1a"] label');
var checkbox = firstItem.querySelector('input[type="checkbox"]');
assert.isTrue(checkbox.checked);
firstItem.click();
setTimeout(function() {
assert.isFalse(checkbox.checked);
// After un-selecting a contact the merge button is enabled because
// there are two selected
assert.isFalse(mergeAction.disabled);
firstItem.click();
setTimeout(function() {
assert.isTrue(checkbox.checked);
// After selecting this contact the merge button is enabled because
// there are three selected
assert.isFalse(mergeAction.disabled);
done();
});
});
});
test('Merge button throws an event ', function(done) {
// We are un-selecting the two first ones
list.querySelector('li[data-uuid="1a"] label').click();
list.querySelector('li[data-uuid="2b"] label').click();
window.addEventListener('merge', function handle(evt) {
window.removeEventListener('merge', handle);
assert.equal(Object.keys(evt.detail.checkedContacts).length, 1);
done();
});
mergeAction.click();
});
});
});
});

View file

@ -0,0 +1,57 @@
/* global Contacts, MockContactsList, MockLazyLoader, MockMultipleSelectHTML,
MockMatcher, MockVCardReader */
'use strict';
requireApp('communications/contacts/test/unit/mock_contacts_list.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
requireApp('communications/contacts/test/unit/mock_multiple_select_dom.js');
requireApp('communications/contacts/test/unit/mock_contacts_match.js');
requireApp('communications/contacts/test/unit/mock_vcard_reader.js');
requireApp('communications/contacts/js/views/multiple_select.js');
window.contacts = window.contacts || {};
suite('MultipleSelect view', function() {
var realLazyLoader,
realMatcher;
suiteSetup(function() {
realLazyLoader = window.LazyLoader;
window.LazyLoader = MockLazyLoader;
document.body.innerHTML = MockMultipleSelectHTML;
Contacts.MultipleSelect.init();
realMatcher = window.Matcher;
window.Matcher = MockMatcher;
});
suiteTeardown(function() {
window.LazyLoader = realLazyLoader;
window.Matcher = realMatcher;
});
test('Adding contacts to the view', function() {
const FILENAME = 'FileName.vcf';
Contacts.MultipleSelect.render(MockContactsList(), {}, FILENAME);
var title = document.getElementById('multiple-select-view-title');
assert.equal(title.textContent, FILENAME);
var contacts = document.querySelectorAll('li');
assert.equal(contacts.length, 3);
assert.equal(contacts[0].textContent.trim(), 'Pepito A\n Test');
assert.equal(contacts[1].textContent.trim(), 'Pepito BA\n Test');
assert.equal(contacts[2].textContent.trim(), 'Antonio CC\n Test');
});
test('Loading contacts from vCard cursor', function(done) {
var mockReader = new MockVCardReader(MockContactsList());
Contacts.MultipleSelect.render([], mockReader.getAll());
done(() => {
assert.equal(document.querySelectorAll('li').length, 3);
});
});
});

View file

@ -0,0 +1,692 @@
'use strict';
/* global contacts */
/* global Contacts */
/* global MockMozContacts */
/* global MockContactsIndexHtml */
/* global MockCookie */
/* global MockgetDeviceStorage */
/* global MocksHelper */
/* global MockIccManager */
/* global MockImportStatusData */
/* global MockMozL10n */
/* global MockNavigatorMozMobileConnections */
/* global MockNavigatorSettings */
/* global MockSdCard */
/* global MockLoader */
/* global utils */
/* global ICE */
require('/shared/js/lazy_loader.js');
require('/shared/js/contacts/import/utilities/misc.js');
require('/shared/js/contacts/utilities/event_listeners.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js');
require('/shared/test/unit/mocks/mock_iccmanager.js');
require('/shared/test/unit/mocks/mock_sdcard.js');
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/test/unit/mock_service_extensions.js');
requireApp('communications/contacts/test/unit/mock_cache.js');
requireApp('communications/contacts/test/unit/mock_contacts_index.html.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
requireApp('communications/contacts/test/unit/mock_import_status_data.js');
requireApp('communications/contacts/test/unit/mock_asyncstorage.js');
requireApp('communications/contacts/test/unit/mock_cookie.js');
requireApp('communications/contacts/test/unit/mock_get_device_storage.js');
requireApp('communications/contacts/test/unit/mock_icc_helper.js');
requireApp('communications/contacts/test/unit/mock_loader.js');
require('/shared/test/unit/mocks/mock_confirm_dialog.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
requireApp('communications/contacts/test/unit/mock_l10n.js');
requireApp('communications/contacts/js/utilities/icc_handler.js');
requireApp('communications/contacts/js/utilities/sim_dom_generator.js');
requireApp('communications/contacts/js/navigation.js');
requireApp('communications/contacts/js/utilities/normalizer.js');
requireApp('communications/contacts/js/views/settings.js');
if (!window._) { window._ = null; }
if (!window.utils) { window.utils = null; }
if (!navigator.onLine) { navigator.onLine = null; }
if (!navigator.mozContacts) { navigator.mozContacts = null; }
if (!navigator.mozIccManager) { navigator.mozIccManager = null; }
if (!navigator.mozMobileConnections) { navigator.mozMobileConnections = null; }
if (!window.Rest) {
window.Rest = null;
}
window.self = null;
var real_,
realMozL10n,
realMozContacts,
realUtils,
realCookie,
realOnLine,
realMozIccManager,
realMozMobileConnections,
realLoader;
if (!window.realMozContacts) {
realMozContacts = null;
}
if (!window.realMozIccManager) {
realMozIccManager = null;
}
var mocksHelperForContactSettings = new MocksHelper([
'ExtServices',
'asyncStorage',
'Cache',
'Contacts',
'IccHelper',
'ImportStatusData'
]);
mocksHelperForContactSettings.init();
suite('Contacts settings >', function() {
var realDeviceStorage;
var mocksHelper = mocksHelperForContactSettings;
suiteSetup(function() {
mocksHelper.suiteSetup();
realUtils = window.utils;
realOnLine = Object.getOwnPropertyDescriptor(navigator, 'onLine');
Object.defineProperty(navigator, 'onLine', {
fakeOnLine: false,
configurable: true,
get: function() { return this.fakeOnLine; },
set: function(status) { this.fakeOnLine = status; }
});
realDeviceStorage = navigator.getDeviceStorage;
navigator.getDeviceStorage = MockgetDeviceStorage;
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockMozL10n;
real_ = window._;
window._ = MockMozL10n.get;
realCookie = window.utils && window.utils.cookie;
window.utils = window.utils || {};
window.utils.cookie = MockCookie;
realLoader = window.Loader;
window.Loader = MockLoader;
if (!window.utils) {
window.utils = { sdcard: MockSdCard };
} else {
window.utils.sdcard = MockSdCard;
}
window.utils.overlay = {
show: function() {},
showMenu: function() {}
};
window.utils.status = {
show: function() {}
};
document.body.innerHTML = MockContactsIndexHtml;
});
suiteTeardown(function() {
window._ = real_;
window.utils = realUtils;
window.utils.cookie = realCookie;
window.Loader = realLoader;
if (realOnLine) {
Object.defineProperty(navigator, 'onLine', realOnLine);
}
navigator.getDeviceStorage = realDeviceStorage;
navigator.mozL10n = realMozL10n;
mocksHelper.suiteTeardown();
});
suite('DSDS DOM support', function() {
var spyL10n;
// This test sets an scenario of two sim cards
suiteSetup(function() {
realMozMobileConnections = navigator.mozMobileConnections;
navigator.mozMobileConnections = MockNavigatorMozMobileConnections;
Contacts.showStatus = utils.status.show;
realMozIccManager = navigator.mozIccManager;
navigator.mozIccManager = new MockIccManager();
// Add to facke iccs
navigator.mozIccManager.iccIds[0] = 0;
navigator.mozIccManager.iccIds[1] = 1;
spyL10n = sinon.spy(navigator.mozL10n, 'setAttributes');
});
suiteTeardown(function() {
navigator.mozMobileConnections = realMozMobileConnections;
spyL10n.restore();
});
setup(function() {
while (navigator.mozMobileConnections.length !== 0) {
navigator.mozMobileConnections.mRemoveMobileConnection();
}
var conn1 = new window.MockMobileconnection();
conn1.iccId = 0;
conn1.iccInfo = {
'iccid': 0
};
navigator.mozMobileConnections.mAddMobileConnection(conn1, 0);
var conn2 = new window.MockMobileconnection();
conn2.iccId = 1;
conn2.iccInfo = {
'iccid': 1
};
navigator.mozMobileConnections.mAddMobileConnection(conn2, 1);
contacts.Settings.init();
});
test('Check number of import buttons appearing', function() {
// Check that we generated two sim import buttons
var importButton0 = document.querySelector('#import-sim-option-0');
var importButton1 = document.querySelector('#import-sim-option-1');
assert.isNotNull(importButton0);
assert.isNotNull(importButton1);
// We test as well that the l10NIds are correctly set
assert.equal(spyL10n.args[0][1], 'simNumberNoMSISDN');
assert.equal(spyL10n.args[1][1], 'simNumberNoMSISDN');
});
test('Check number of export buttons appearing', function() {
assert.isNotNull(document.querySelector('#export-sim-option-0'));
assert.isNotNull(document.querySelector('#export-sim-option-1'));
});
test('Check one sim inserted in slot 0', function() {
// Modify the iccManager to return null when asking for slot 1
var stub = sinon.stub(navigator.mozIccManager, 'getIccById',
function(id) {
if (id == 1) {
return null;
}
return {
'iccInfo': {
'iccid': id
}
};
}
);
contacts.Settings.init();
assert.isNotNull(document.querySelector('#import-sim-option-0'));
assert.isNotNull(document.querySelector('#export-sim-option-0'));
assert.isNull(document.querySelector('#import-sim-option-1'));
assert.isNull(document.querySelector('#export-sim-option-1'));
stub.restore();
});
test('Check one sim inserted in slot 1', function() {
// Modify the iccManager to return null when asking for slot 0
var stub = sinon.stub(navigator.mozIccManager, 'getIccById',
function(id) {
if (id === 0) {
return null;
}
return {
'iccInfo': {
'iccid': id
}
};
}
);
contacts.Settings.init();
assert.isNotNull(document.querySelector('#import-sim-option-1'));
assert.isNotNull(document.querySelector('#export-sim-option-1'));
assert.isNull(document.querySelector('#import-sim-option-0'));
assert.isNull(document.querySelector('#export-sim-option-0'));
stub.restore();
});
});
suite('Export options', function() {
var oldCheckStorageCard;
suiteSetup(function() {
oldCheckStorageCard = utils.sdcard.checkStorageCard;
utils.sdcard.checkStorageCard = function() { return true; };
});
setup(function() {
contacts.Settings.init();
mocksHelper.suiteSetup();
realMozContacts = navigator.mozContacts;
navigator.mozContacts = MockMozContacts;
});
test('If there are no contacts, export option is disabled', function() {
navigator.mozContacts.number = 0;
contacts.Settings.refresh();
var exportContacts = document.
getElementById('exportContacts').firstElementChild;
assert.equal(exportContacts.getAttribute('disabled'), 'disabled');
});
test('If there are contacts, export option is enabled', function() {
navigator.mozContacts.number = 100;
contacts.Settings.refresh();
var exportContacts = document.
getElementById('exportContacts').firstElementChild;
assert.isNull(exportContacts.getAttribute('disabled'));
});
suiteTeardown(function() {
mocksHelper.suiteTeardown();
navigator.mozContacts = realMozContacts;
utils.sdcard.checkStorageCard = oldCheckStorageCard;
});
});
suite('SD Card Availability>', function() {
var importSection,
importError,
importSDButton;
var exportSection,
exportError,
exportSDButton;
var noCardErrorImport = 'noMemoryCardMsg',
noCardErrorExport = 'noMemoryCardMsgExport',
umsEnabledError = 'memoryCardUMSEnabled';
function shareSDCard() {
utils.sdcard.status = MockSdCard.SHARED;
}
function connectSDCard() {
utils.sdcard.status = MockSdCard.AVAILABLE;
}
function disconnectSDCard() {
utils.sdcard.status = MockSdCard.NOT_AVAILABLE;
}
suiteSetup(function() {
importSection = document.getElementById('import-sd-option');
importSDButton = importSection.firstElementChild;
importError = importSection.querySelector('p.error-message');
exportSection = document.getElementById('export-sd-option');
exportSDButton = exportSection.firstElementChild;
exportError = exportSection.querySelector('p.error-message');
contacts.Settings.init();
});
suite('SD shared >', function() {
setup(function() {
shareSDCard();
});
test('import button should be disabled', function() {
assert.isTrue(importSDButton.hasAttribute('disabled'));
});
test('import error message should be shown', function() {
assert.isTrue(importSection.classList.contains('error'));
});
test('import error message should be correct (usb storage enabled)',
function() {
assert.equal(importError.getAttribute('data-l10n-id'),
umsEnabledError);
});
test('export button should be disabled', function() {
assert.isTrue(exportSDButton.hasAttribute('disabled'));
});
test('export error message should be shown', function() {
assert.isTrue(exportSection.classList.contains('error'));
});
test('export error message should be correct (usb storage enabled)',
function() {
assert.equal(exportError.getAttribute('data-l10n-id'),
umsEnabledError);
});
});
suite('SD not available >', function() {
setup(function() {
disconnectSDCard();
});
test('import button should be disabled', function() {
assert.isTrue(importSDButton.hasAttribute('disabled'));
});
test('import error message should be shown', function() {
assert.isTrue(importSection.classList.contains('error'));
});
test('import error message should be correct (insert SD card)',
function() {
assert.equal(importError.getAttribute('data-l10n-id'),
noCardErrorImport);
});
test('export button should be disabled', function() {
assert.isTrue(exportSDButton.hasAttribute('disabled'));
});
test('export error message should be shown', function() {
assert.isTrue(exportSection.classList.contains('error'));
});
test('export error message should be correct (insert SD card)',
function() {
assert.equal(exportError.getAttribute('data-l10n-id'),
noCardErrorExport);
});
});
suite('SD not present >', function() {
var deviceStorages, deviceStorage;
suiteSetup(function() {
deviceStorages = utils.sdcard.deviceStorages;
deviceStorage = utils.sdcard.deviceStorage;
utils.sdcard.deviceStorages = [];
utils.sdcard.deviceStorage = null;
});
suiteTeardown(function() {
utils.sdcard.deviceStorages = deviceStorages;
utils.sdcard.deviceStorage = deviceStorage;
});
test('import button should be disabled', function() {
assert.isTrue(importSDButton.hasAttribute('disabled'));
});
test('export button should be disabled', function() {
assert.isTrue(exportSDButton.hasAttribute('disabled'));
});
});
suite('SD available >', function() {
setup(function() {
connectSDCard();
});
test('import button should be enabled', function() {
assert.isFalse(importSDButton.hasAttribute('disabled'));
});
test('import error message should be hidden', function() {
assert.isFalse(importSection.classList.contains('error'));
});
test('export button should be enabled', function() {
assert.isFalse(exportSDButton.hasAttribute('disabled'));
});
test('export error message should be hidden', function() {
assert.isFalse(exportSection.classList.contains('error'));
});
});
});
suite('Timestamp Import', function() {
var realMozSettings;
suiteSetup(function(done) {
realMozSettings = navigator.mozSettings;
navigator.mozSettings = MockNavigatorSettings;
navigator.mozSettings.mSettings['locale.hour12'] = true;
require('/shared/js/date_time_helper.js', done);
});
suiteTeardown(function() {
navigator.mozSettings = realMozSettings;
});
var timestamps = {
'gmail': Date.now(),
'live': Date.now() - 24 * 60 * 60 * 1000,
'sd': Date.now() - 48 * 60 * 60 * 1000,
'sim': Date.now() - 72 * 60 * 60 * 1000
};
setup(function(done) {
// Restore previous tainted html
document.body.innerHTML = MockContactsIndexHtml;
contacts.Settings.init();
MockImportStatusData.clear().then(done, done);
});
function assertNoContactsFrom(source) {
var time = document.getElementById('import-' + source + '-option')
.querySelector('time');
assert.equal(time.textContent, '');
assert.isNull(time.getAttribute('datetime'));
}
test('No contacts imported yet', function() {
var sources = Object.keys(timestamps);
for (var i = 0, l = sources.length; i < l; i++) {
assertNoContactsFrom(sources[i]);
}
});
function assertContactsImportedFrom(source, done, withTime) {
var importElm = document.getElementById('import-' + source + '-option');
var time = importElm.querySelector('time');
var test = function() {
assert.equal(time.getAttribute('datetime'),
(new Date(timestamps[source])).toLocaleString());
assert.equal(time.textContent, utils.time.pretty(timestamps[source]));
if (withTime) {
var timeString = new Date(timestamps[source]).toLocaleString(
navigator.languages, {
hour12: navigator.mozHour12,
hour: 'numeric',
minute: 'numeric',
});
assert.isTrue(time.textContent.indexOf(timeString) != -1);
}
observer.disconnect();
};
var observer = new MutationObserver(function(){
test();
done();
});
observer.observe(time, {attributes: true});
MockImportStatusData.put(source + '_last_import_timestamp',
timestamps[source])
.then(function() {
contacts.Settings.updateTimestamps();
});
}
test('Contacts imported from SD', function(done) {
assertContactsImportedFrom('sd', done);
});
test('Contacts imported from sim', function(done) {
assertContactsImportedFrom('sim', done);
});
test('Contacts imported from Gmail', function(done) {
assertContactsImportedFrom('gmail', done);
});
test('Contacts imported from Live', function(done) {
assertContactsImportedFrom('live', done);
});
test('Test check 12 hour format', function(done) {
assertContactsImportedFrom('gmail', done, true);
});
test('Test check 24 hour format', function(done) {
navigator.mozSettings.mSettings['locale.hour12'] = false;
navigator.mozSettings.mTriggerObservers('locale.hour12',
{'settingValue': false});
assertContactsImportedFrom('gmail', done, true);
});
});
suite('Network status change', function() {
var importGmailOption,
importLiveOption;
suiteSetup(function() {
contacts.Settings.init();
importGmailOption = document.getElementById('import-gmail-option');
importLiveOption = document.getElementById('import-live-option');
});
suite('Online', function() {
setup(function() {
navigator.onLine = true;
var customEvent = new CustomEvent('online');
window.dispatchEvent(customEvent);
});
test('Import Gmail enabled', function() {
assert.isFalse(
importGmailOption.firstElementChild.hasAttribute('disabled'));
});
test('Import Gmail error hidden', function() {
assert.isFalse(
importGmailOption.classList.contains('error'));
});
test('Import Live enabled', function() {
assert.isFalse(
importLiveOption.firstElementChild.hasAttribute('disabled'));
});
test('Import Live error hidden', function() {
assert.isFalse(
importLiveOption.classList.contains('error'));
});
});
suite('Offline', function() {
setup(function() {
navigator.onLine = false;
var customEvent = new CustomEvent('offline');
window.dispatchEvent(customEvent);
});
test('Import Gmail disabled', function() {
assert.isTrue(
importGmailOption.firstElementChild.hasAttribute('disabled'));
});
test('Import Gmail error shown', function() {
assert.isTrue(
importGmailOption.classList.contains('error'));
});
test('Import Live disabled', function() {
assert.isTrue(
importLiveOption.firstElementChild.hasAttribute('disabled'));
});
test('Import Live error shown', function() {
assert.isTrue(
importLiveOption.classList.contains('error'));
});
});
});
suite('Bulk Delete options', function() {
setup(function() {
contacts.Settings.init();
mocksHelper.suiteSetup();
realMozContacts = navigator.mozContacts;
navigator.mozContacts = MockMozContacts;
});
test('If no contacts, Bulk Delete option is disabled', function() {
navigator.mozContacts.number = 0;
contacts.Settings.refresh();
var bulkDelContacts = document.
getElementById('bulkDelete');
assert.equal(bulkDelContacts.getAttribute('disabled'), 'disabled');
});
test('If there are contacts, bulk Delete option is enabled', function() {
navigator.mozContacts.number = 100;
contacts.Settings.refresh();
var bulkDelContacts = document.
getElementById('bulkDelete');
assert.isNull(bulkDelContacts.getAttribute('disabled'));
});
suiteTeardown(function() {
mocksHelper.suiteTeardown();
navigator.mozContacts = realMozContacts;
});
});
suite('ICE options', function() {
var iceContacts;
suiteSetup(function() {
iceContacts = document.getElementById('set-ice');
});
setup(function() {
contacts.Settings.init();
mocksHelper.suiteSetup();
realMozContacts = navigator.mozContacts;
navigator.mozContacts = MockMozContacts;
});
test('If no contacts, ICE contacts option is disabled', function() {
navigator.mozContacts.number = 0;
contacts.Settings.refresh();
assert.isTrue(iceContacts.disabled);
});
test('If there are contacts, ICE contacts option is enabled', function() {
navigator.mozContacts.number = 100;
contacts.Settings.refresh();
assert.isFalse(iceContacts.disabled);
});
test('Pressing the ICE button should init ICE module', function(done) {
contacts.Settings.showICEScreen(function() {
assert.equal(
contacts.Settings.navigation.currentView(),
'ice-settings'
);
assert.ok(ICE.initialized);
done();
});
});
suiteTeardown(function() {
mocksHelper.suiteTeardown();
navigator.mozContacts = realMozContacts;
});
});
});

View file

@ -0,0 +1,195 @@
/* global LazyLoader, VCardLoadController, VCardLoadUI */
/* global MockContactsList, VCardHandler */
'use strict';
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/js/lazy_loader.js');
requireApp('communications/contacts/views/vcard_load/js/boot.js');
requireApp('communications/contacts/views/vcard_load/js/vcard_handler.js');
requireApp('communications/contacts/test/unit/mock_contacts_list.js');
suite('Open VCards Boot', function() {
var lazyLoadStub;
suiteSetup(function() {
lazyLoadStub = sinon.stub(LazyLoader, 'load', function(files) {
return {
then: function(callback) {
callback();
}
};
});
});
suiteTeardown(function() {
LazyLoader.load.restore();
});
setup(function() {
loadBodyHTML('/contacts/views/vcard_load/vcard_load.html');
});
teardown(function() {
document.body.innerHTML = '';
});
suite('DOMContentLoaded', function() {
setup(function() {
window.dispatchEvent(new CustomEvent('DOMContentLoaded'));
});
test(' > LazyLoader must be called 2 times', function() {
// We must have 2 calls to LazyLoader
assert.isTrue(lazyLoadStub.calledTwice);
});
test(' > First call must ensure localization', function() {
// First of all we need to bring l10n library for localization
var firstCall = lazyLoadStub.getCall(0);
assert.isTrue(Array.isArray(firstCall.args[0]));
assert.equal(firstCall.args[0][0], '/shared/js/l10n.js');
});
test(' > Second call must load the template', function() {
// Secondly we lazy load the panel, so the param must be the element
// of the DOM to fill with the template
var secondCall = lazyLoadStub.getCall(1);
assert.equal(secondCall.args[0][0].id, 'multiple-select-view');
});
});
suite('Loaded', function() {
var realLoadController, realLoadUI, realMozSetMessage;
suiteSetup(function() {
realLoadController = window.VCardLoadController;
realLoadUI = window.VCardLoadUI;
window.VCardLoadController = {
init: function foo() {}
};
window.VCardLoadUI = {
init: function foo() {}
};
realMozSetMessage = navigator.mozSetMessageHandler;
navigator.mozSetMessageHandler = function foo() {};
});
suiteTeardown(function() {
window.VCardLoadController = realLoadController;
window.VCardLoadUI = realLoadUI;
navigator.mozSetMessageHandler = realMozSetMessage;
realLoadController = null;
realLoadUI = null;
realMozSetMessage = null;
});
test(' > Controller is initialized', function() {
var loadSpy = this.sinon.spy(VCardLoadController, 'init');
window.dispatchEvent(new CustomEvent('load'));
assert.isTrue(loadSpy.calledOnce);
});
test(' > UI is initialized', function() {
var loadUISpy = this.sinon.spy(VCardLoadUI, 'init');
window.dispatchEvent(new CustomEvent('load'));
assert.isTrue(loadUISpy.calledOnce);
});
});
suite('Activity', function() {
var realLoadController, realLoadUI, realMozSetMessage, contacts;
var fakeActivity = {
source: {
data: {
allowSave: true,
blob: null,
filename:
'/apps/communications/contacts/test/unit/vcards/vcard_21.vcf',
type:'text/x-vcard'
}
}
};
suiteSetup(function() {
realLoadController = window.VCardLoadController;
realLoadUI = window.VCardLoadUI;
window.VCardLoadController = {
init: function init() {},
setActivity: function setActivity() {}
};
window.VCardLoadUI = {
init: function init() {},
render: function render() {}
};
contacts = MockContactsList();
sinon.stub(VCardHandler, 'handle', function(activity) {
return {
then: function(callback) {
callback(contacts);
}
};
});
sinon.stub(VCardHandler, 'getFileName', function(filename) {
return 'vcard_21.vcf';
});
});
suiteTeardown(function() {
window.VCardLoadController = realLoadController;
window.VCardLoadUI = realLoadUI;
realLoadController = null;
realLoadUI = null;
realMozSetMessage = null;
contacts = null;
});
test(' > Listener to activity is set properly', function(done) {
this.sinon.stub(navigator, 'mozSetMessageHandler', function() {
done();
});
window.dispatchEvent(new CustomEvent('load'));
});
test(' > UI is updated properly with activity params', function(done) {
var loadUIRenderSpy = this.sinon.spy(VCardLoadUI, 'render');
this.sinon.stub(navigator, 'mozSetMessageHandler',
function(name, callback) {
callback(fakeActivity);
assert.isTrue(loadUIRenderSpy.called);
assert.equal(
loadUIRenderSpy.getCall(0).args[0].length,
contacts.length
);
assert.equal(
loadUIRenderSpy.getCall(0).args[1],
'vcard_21.vcf'
);
done();
}
);
window.dispatchEvent(new CustomEvent('load'));
});
test(' > Activity is cached in Controller', function(done) {
var loadActivitySpy = this.sinon.spy(VCardLoadController, 'setActivity');
this.sinon.stub(navigator, 'mozSetMessageHandler',
function(name, callback) {
callback(fakeActivity);
assert.isTrue(loadActivitySpy.called);
done();
}
);
window.dispatchEvent(new CustomEvent('load'));
});
});
});

View file

@ -0,0 +1,69 @@
/* global VCardHandler, MockVCardReader */
'use strict';
requireApp('communications/contacts/views/vcard_load/js/vcard_handler.js');
requireApp('communications/contacts/test/unit/mock_vcard_reader.js');
suite('VCardHandler', function() {
const VCARD_PATH = '/apps/communications/contacts/test/unit/vcards/';
const VCARD_FILE =
'/apps/communications/contacts/test/unit/vcards/vcard_21.vcf';
const VCARD_FILE_MULTIPLE =
'/apps/communications/contacts/test/unit/vcards/vcard_21_multiple.vcf';
var realVCardReader;
var fakeActivity = {
source: {
data: {
allowSave: true,
blob: null,
filename: VCARD_FILE,
type:'text/x-vcard'
}
}
};
setup(function() {
loadBodyHTML('/contacts/views/vcard_load/vcard_load.html');
realVCardReader = window.VCardReader;
window.VCardReader = MockVCardReader;
});
teardown(function() {
window.VCardReader = realVCardReader;
realVCardReader = null;
});
suite('handle', function() {
function initializeVCardReader(filename) {
return new Promise(function(resolve, reject) {
var req = new XMLHttpRequest();
var vCardUrl = VCARD_PATH + filename;
req.open('get', vCardUrl, true);
req.send();
req.onload = function() {
resolve(this.responseText);
};
});
}
test(' > vCard is parsed correctly', function(done) {
initializeVCardReader('vcard_21_multiple.vcf').then(function(vCardText) {
var blob = new Blob([vCardText], {type: 'text/x-vCard'});
fakeActivity.source.data.blob = blob;
VCardHandler.handle(fakeActivity).then(function(contacts) {
assert.equal(contacts.length, 6);
done();
});
});
});
});
suite('getFileName', function() {
test(' > vCard filename', function() {
var name = VCardHandler.getFileName(VCARD_FILE_MULTIPLE);
assert.equal(name, 'vcard_21_multiple.vcf');
});
});
});

View file

@ -0,0 +1,106 @@
/* global LazyLoader, ContactsService, MockMatcher, MockContactsList */
/* global VCardLoadController */
'use strict';
require('/shared/js/lazy_loader.js');
requireApp('communications/contacts/test/unit/mock_contacts_match.js');
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/test/unit/mock_contacts_list.js');
requireApp(
'communications/contacts/views/vcard_load/js/vcard_load_controller.js');
suite('VCardLoadController', function() {
var stubLazyLoader;
var stubContactServiceSave;
var contacts;
var realMatcher;
var fakeActivity = {
source: {
data: {
allowSave: true,
blob: null,
filename:
'/apps/communications/contacts/test/unit/vcards/vcard_21.vcf',
type:'text/x-vcard'
}
},
postResult: function foo() {}
};
suiteSetup(function() {
VCardLoadController.init();
VCardLoadController.setActivity(fakeActivity);
realMatcher = window.Matcher;
window.Matcher = MockMatcher;
});
suiteTeardown(function() {
window.Matcher = realMatcher;
realMatcher = null;
});
setup(function() {
contacts = MockContactsList();
stubContactServiceSave = this.sinon.stub(ContactsService, 'save',
function(contact, onerror) {
onerror(null);
});
stubLazyLoader = this.sinon.stub(LazyLoader, 'load',
function(files, callback) {
callback();
});
});
teardown(function() {
stubLazyLoader = null;
stubContactServiceSave = null;
contacts = null;
});
suite('Close button', function() {
test(' > must call close when an event is recived', function(done) {
var spy = this.sinon.spy(fakeActivity, 'postResult');
window.addEventListener('closeAction', function() {
assert.isTrue(spy.calledOnce);
done();
});
window.dispatchEvent(new CustomEvent('closeAction'));
});
});
suite('Import all button', function() {
var clock, spyMatcher, spyPostResult, spyRemoveEventListener;
setup(function() {
clock = this.sinon.useFakeTimers();
spyMatcher = this.sinon.spy(window.Matcher, 'match');
spyPostResult = this.sinon.spy(fakeActivity, 'postResult');
spyRemoveEventListener = this.sinon.spy(window, 'removeEventListener');
});
test(' > must import all contacts received', function(done) {
window.addEventListener('saveAction', function(evt) {
console.info(evt.detail);
assert.equal(evt.detail.contactsToImport.length, contacts.length);
assert.isTrue(spyMatcher.calledThrice);
assert.isTrue(stubContactServiceSave.calledThrice);
assert.isTrue(spyRemoveEventListener.calledTwice);
assert.equal(spyRemoveEventListener.getCall(0).args[0], 'closeAction');
assert.equal(spyRemoveEventListener.getCall(1).args[0], 'saveAction');
clock.tick(3000);
assert.isTrue(spyPostResult.calledOnce);
done();
});
window.dispatchEvent(new CustomEvent('saveAction', {
'detail': {
'contactsToImport': contacts
}
}));
});
});
});

View file

@ -0,0 +1,109 @@
/* global LazyLoader, VCardLoadUI, MockContactsList, MockL10n, utils */
'use strict';
require('/shared/js/contacts/import/utilities/status.js');
require('/shared/test/unit/load_body_html_helper.js');
require('/shared/js/lazy_loader.js');
require('/shared/test/unit/mocks/mock_l10n.js');
requireApp('communications/contacts/views/vcard_load/js/vcard_load_ui.js');
requireApp('communications/contacts/test/unit/mock_contacts_list.js');
suite('VCardLoadUI', function() {
var realMozL10n, contacts, stubUtilsStatus;
suiteSetup(function(done) {
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
// Load HTML
loadBodyHTML('/contacts/views/vcard_load/vcard_load.html');
// Add hook to template to "head"
var importHook = document.createElement('link');
importHook.id = 'multiple-select-import-link';
importHook.setAttribute('rel', 'import');
importHook.setAttribute('href', '/contacts/elements/multiple_select.html');
document.head.appendChild(importHook);
console.log(document.body.innerHTML);
// Fill the HTML
LazyLoader.load(
[
document.getElementById('multiple-select-view')
],
function() {
contacts = MockContactsList();
VCardLoadUI.init();
done();
}
);
});
suiteTeardown(function() {
navigator.mozL10n = realMozL10n;
realMozL10n = null;
window.VCardLoadUI = null;
document.body.innerHTML = '';
});
setup(function() {
stubUtilsStatus = this.sinon.stub(utils.status, 'show', function show() {});
});
suite('Close button', function() {
test(' > must dispatch an event when clicked', function(done) {
window.addEventListener('closeAction', function() {
done();
});
var clickEvent = new CustomEvent('action');
document.querySelector('#multiple-select-view-header').
dispatchEvent(clickEvent);
});
});
suite('Import all button', function() {
test(' > must dispatch an event when clicked', function(done) {
window.addEventListener('saveAction', function(evt) {
done();
});
document.querySelector('#save-button').click();
});
});
suite('Show status action', function() {
test(' > must handle showStatusAction event', function(done) {
window.addEventListener('showStatusAction', function(evt) {
assert.isTrue(stubUtilsStatus.calledOnce);
done();
});
window.dispatchEvent(new CustomEvent('showStatusAction', {
'detail': {
'numDups': 1,
'importedContacts': 1
}
}));
});
});
suite('Render', function() {
test(' > contacts list must be rendered correctly', function() {
const FILENAME = 'FileName.vcf';
VCardLoadUI.render(contacts, FILENAME);
var title = document.getElementById('multiple-select-view-title');
assert.equal(title.textContent, FILENAME);
var contactList = document.getElementById('multiple-select-container');
var listItems = contactList.querySelectorAll('li');
assert.equal(listItems.length, 3);
assert.equal(listItems[0].textContent.trim(), 'Pepito A\n Test');
assert.equal(listItems[1].textContent.trim(), 'Pepito BA\n Test');
assert.equal(listItems[2].textContent.trim(), 'Antonio CC\n Test');
});
});
});