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,19 @@
{
"extends": "../../../../../.jshintrc",
"nonew": false,
"predef": [
"__dirname",
"module",
"suite",
"test",
"setup",
"teardown",
"suiteSetup",
"suiteTeardown",
"marionette",
"marionetteScriptFinished",
"require",
"define",
"requireApp"
]
}

View file

@ -0,0 +1,253 @@
'use strict';
/* global MozActivity */
var Contacts = require('./lib/contacts');
var Dialer = require('../../../dialer/test/marionette/lib/dialer');
var Sms = require('./lib/sms');
var assert = require('assert');
var fs = require('fs');
marionette('Contacts > Activities', function() {
var client = marionette.client({
profile: Contacts.config,
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
var dialerSubject;
var smsSubject;
var smsSelectors;
var subject;
var selectors;
setup(function() {
subject = new Contacts(client);
selectors = Contacts.Selectors;
dialerSubject = new Dialer(client);
smsSubject = new Sms(client);
smsSelectors = Sms.Selectors;
});
suite('open text/vcard activity', function() {
setup(function() {
smsSubject.launch(); // We open some app to start a Marionette session.
});
function getListItems() {
return client.executeScript(function() {
return document.querySelectorAll('#multiple-select-container li');
});
}
test('> with only one contact', function() {
client.executeScript(function(vCardFile) {
new MozActivity({
name: 'open',
data: {
type: 'text/vcard',
filename: 'vcard_4.vcf',
blob: new Blob([vCardFile], {type: 'text/vcard'})
}
});
}, [fs.readFileSync(__dirname + '/data/vcard_4.vcf', 'utf8')]);
var iframe = 'iframe[src="' + Contacts.URL +
'/contacts/views/vcard_load/vcard_load.html"]';
client.switchToFrame();
client.switchToFrame(client.findElement(iframe), {'focus': true});
client.helper.waitForElement(selectors.multipleSelectSave);
assert.ok(getListItems().length === 1); // vcard has one element
});
test('> with multiple contacts', function() {
client.executeScript(function(vCardFile) {
new MozActivity({
name: 'open',
data: {
type: 'text/vcard',
filename: 'vcard_21_multiple.vcf',
blob: new Blob([vCardFile], {type: 'text/vcard'})
}
});
}, [fs.readFileSync(__dirname + '/data/vcard_21_multiple.vcf', 'utf8')]);
var iframe = 'iframe[src="' + Contacts.URL +
'/contacts/views/vcard_load/vcard_load.html"]';
client.switchToFrame();
client.switchToFrame(client.findElement(iframe), {'focus': true});
client.helper.waitForElement(selectors.multipleSelectSave);
assert.ok(getListItems().length === 2); // vcard has one element
});
});
suite('webcontacts/contact activity', function() {
// Disabling these tests by now due to we need a way to switch to an
// activity instead of switching to an app, due to paths can differ.
// More info in [1].
// These test must be recovered once this bug will be landed.
// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1140344#c9
test.skip('a contact with duplicate number shows merge page', function() {
subject.launch();
subject.addContact({
givenName: 'From Contacts App',
tel: 1111
});
client.apps.close(Contacts.URL, 'contacts');
dialerSubject.launch();
// Dialer keys don't work in b2g desktop for some reason yet,
// So just manually fire off the activity
client.executeScript(function() {
new MozActivity({
name: 'new',
data: {
type: 'webcontacts/contact',
params: {
'tel': 1111
}
}
});
});
client.switchToFrame();
client.apps.switchToApp(Contacts.URL, 'contacts');
subject.enterContactDetails({
givenName: 'From Dialer Activity'
});
this.waitForFormTransition();
client.switchToFrame(client.findElement(selectors.duplicateFrame));
var duplicateHeader = client.helper.
waitForElement(selectors.duplicateHeader);
var expectedResult = subject.l10n(
'/locales-obj/contacts.matching_contacts.en-US.json',
'duplicatesFoundTitle');
assert.equal(duplicateHeader.text(), expectedResult);
});
});
suite('webcontacts/tel activity', function() {
// Disabling these tests by now due to we need a way to switch to an
// activity instead of switching to an app, due to paths can differ.
// More info in [1].
// These test must be recovered once this bug will be landed.
// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1140344#c9
test.skip('Error message when no contacts', function() {
smsSubject.launch();
// Launch the activity directly as mozSms has problems
// in b2g desktop.
client.executeScript(function() {
new MozActivity({
name: 'pick',
data: {
type: 'webcontacts/tel'
}
});
});
client.switchToFrame();
client.apps.switchToApp(Contacts.URL, 'contacts');
var confirmMsg = client.findElement(selectors.confirmBody);
var expectedResult = subject.l10n(
'/locales-obj/contacts.index.en-US.json',
'noContactsActivity2');
assert.equal(confirmMsg.text(), expectedResult);
});
test.skip('Error message selected contact has no number', function() {
subject.launch();
subject.addContact({
givenName: 'No Phone Number'
});
client.apps.close(Contacts.URL, 'contacts');
smsSubject.launch();
client.executeScript(function() {
new MozActivity({
name: 'pick',
data: {
type: 'webcontacts/tel'
}
});
});
client.switchToFrame();
client.apps.switchToApp(Contacts.URL, 'contacts');
client.helper.waitForElement(selectors.bodyReady);
client.helper.waitForElement(selectors.listContactFirst)
.click();
var confirmText = client.helper.waitForElement(selectors.confirmBody)
.text();
var expectedResult = subject.l10n(
'/locales-obj/contacts.index.en-US.json',
'no_contact_phones');
assert.equal(confirmText, expectedResult);
});
});
suite('webcontacts/email activity', function() {
test('Creates only one instance of action menu', function() {
subject.launch();
subject.addContactMultipleEmails({
givenName: 'From Contacts App',
emailFirst: 'first@personal.com',
emailSecond: 'second@personal.com'
});
client.apps.close(Contacts.URL, 'contacts');
smsSubject.launch();
client.executeScript(function() {
new MozActivity({
name: 'pick',
data: {
type: 'webcontacts/email'
}
});
});
client.switchToFrame();
client.apps.switchToApp(Contacts.URL, 'contacts');
client.helper.waitForElement(selectors.bodyReady);
var contact = client.helper.waitForElement(selectors.listContactFirst);
// Simulate two clicks
contact.click();
contact.click();
var emailList = client.helper.waitForElement(selectors.actionMenuList);
var emailChildren = emailList.findElements('button');
assert.equal(emailChildren.length, 3);
});
});
});

View file

@ -0,0 +1,82 @@
'use strict';
var Contacts = require('./lib/contacts');
var assert = require('assert');
marionette('Contacts shortcuts > touch', function() {
var client = marionette.client({ profile: Contacts.config });
var subject;
var selectors;
var actions;
var scrollbar;
var overlay;
function overlayOpacity() {
return subject.getElementStyle(selectors.overlay, 'opacity');
}
setup(function() {
actions = client.loader.getActions();
subject = new Contacts(client);
subject.launch();
selectors = Contacts.Selectors;
subject.addContact();
scrollbar = client.helper.waitForElement(selectors.scrollbar);
});
// Disabled bug 1023908
suite('touch on shortcuts', function() {
test('press/release on scrollbar should show/hide shortcut', function() {
var action = actions.press(scrollbar, 10, 100).perform();
overlay = client.helper.waitForElement(selectors.overlay);
assert.equal(overlay.text().length, 1);
assert.equal(overlayOpacity(), '1');
action.release().perform();
assert.equal(overlay.text().length, 0);
assert.equal(overlayOpacity(), '0');
});
// Disabled bug 1005708
test.skip('shortcut text should change after moving some distance',
function() {
var letter, nextLetter, lastLetter;
var action = actions.press(scrollbar, 10, 100).perform();
overlay = client.helper.waitForElement(selectors.overlay);
letter = overlay.text();
assert.equal(letter.length, 1);
action.moveByOffset(0, 1).perform();
nextLetter = overlay.text();
assert.equal(nextLetter.length, 1);
assert.equal(letter, nextLetter);
action.moveByOffset(0, 50).perform();
lastLetter = overlay.text();
assert.equal(nextLetter.length, 1);
assert.notEqual(lastLetter, nextLetter);
action.release().perform();
});
test('pressing near the last release position should show valid shortcut',
function() {
actions.press(scrollbar, 15, 100).release().perform();
var action = actions.press(scrollbar, 15, 100).perform();
overlay = client.helper.waitForElement(selectors.overlay);
assert.equal(overlay.text().length, 1);
action.release().perform();
});
test('pressing outside the scrollbar should not show shortcut',
function() {
var action = actions.press(scrollbar, -1, 200).perform();
assert.notEqual(overlayOpacity(), '1');
action.release().perform();
});
});
});

View file

@ -0,0 +1,43 @@
'use strict';
window.wrappedJSObject.data = window.wrappedJSObject.data || {};
window.wrappedJSObject.data.fbContactData = {
'uid': '100007887283166',
'name': 'Jander Klander',
'pic_big': 'https://fbcdn-profile-a.akamaihd.net/static-ak/' +
'fbrsrc.php/v2/yL/r/HsTZSDw4avx.gif',
'current_location': null,
'email': [{
'type': ['other'],
'value': 'gtorodelvalle@hotmail.com'
}],
'profile_update_time': 1393974966,
'work': [],
'hometown_location': null,
'_idxFriendsArray': 124,
'familyName': ['Klander'],
'additionalName': '',
'givenName': ['Jander'],
'tel': [{
'type': ['other'],
'value': '+34666666666'
}],
'email1': 'gtorodelvalle@hotmail.com',
'contactPictureUri': 'https://graph.facebook.com/100007887283166/' +
'picture?type=square&width=120&height=120',
'search': 'JanderKlandergtorodelvalle@hotmail.com',
'picwidth': 120,
'picheight': 120,
'_idx_': 0,
'fbInfo': {
'org': [''],
'adr': [],
'shortTelephone': ['666666666'],
'url': [{
'type': ['fb_profile_photo'],
'value': 'https://fbcdn-profile-a.akamaihd.net/static-ak/' +
'rsrc.php/v2/yL/r/HsTZSDw4avx.gif'
}]
}
};

View file

@ -0,0 +1,19 @@
BEGIN:VCARD
VERSION:2.1
N;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:Gump;F=C3=B3rrest
TEL;PREF;VOICE;WORK:(111) 555-1212
ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0ABaytown, LA 30314=0D=0AUnited States of America
EMAIL;PREF;INTERNET:forrestgump@example.com
END:VCARD
BEGIN:VCARD
VERSION:2.1
N;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:Gump2;F=C3=B3rrest2
TEL;PREF;VOICE;WORK:(111) 555-1213
TEL;HOME;VOICE:(404) 555-1213
ADR;WORK;ENCODING=QUOTED-PRINTABLE:;;100 W=C3=A1ters Edge;Baytown;LA;30314;United States of America
LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, LA 30314=0D=0AUnited States of America
ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0ABaytown, LA 30314=0D=0AUnited States of America
EMAIL;PREF;INTERNET:forrestgump@example2.com
END:VCARD

View file

@ -0,0 +1,20 @@
BEGIN:VCARD
VERSION:4.0
N:Gump;Forrest;;;
FN:Forrest Gump
ORG:Bubba Gump Shrimp Co.
TITLE:Shrimp Man
PHOTO:data:image/bmp;base64,Qk02BAAAAAAAADYAAAAoAAAAEAAAAPD///8BACAAAAAAAAAAAABiBwAAYgcAAAAAAAAAAAAA///////////////////////////////////////////////////+///////////////////////////////////////////////////////////////////////V0s3/b21l/1pbVf9ydHL/u8HB//D09f/29vb///////////////////////////////////////Lx8P9ycWv/EhEO/wAAAP8AAgD/Gh0Z/15mav+tt7z/6+zr/////////////v////////////////////////+IiYf/AgYF/wAAAP8AAAD/AAAA/x8iI/88Ojv/e3l6///+/v/////////////////////////////////p5+f/am5z/xoeI/8iKDH/HSIr/ztATP+wtL//sbfC/zI4P//Ky8r///////////////////////7+/f/s7O3/vcDH/2Fkbf8+Qk7/foSW/5Wcsv+5vtP/7ezz//////97g5P/j5Oh//f4+P//////+/v7//Py8v/z8vP/9/j5/8jL2/8zNkD/MDM4/3Nydf/X0+H/0cvZ/7Srrv/Hvrz/h4uX/2Vsf/+5vcP/5OXj//Tz8//5+fn///////7+/v+eoa3/NjxE/2Vnaf9vbGr/pqSt/+ro8v+xqrP/6ePi/4SLnP85QE//e4SQ/5CWmv////////////z8+//X1tf/kZWc/09UXP9hY2f/TEhL/62twP/o6vP/n5ij/9LM0f+WnbX/PkRS/4CFkv99gor//f39//j39//19PP/+Pj3//////+ysrj/cnqP/21viv/c2On/9PL3//Hv+v/l5/T/x8jd/7Cvv//Y0NX/5NzD//X19f/7+/v///////Py9v/W3uf/z9Xa/4WFm/9UVGn/tK3D/+/o8v/Y0uD/9fL8/+3q9f/w7fn/+/n+///////////////////////08/b/2eLo/8XT2P9zdYb/XFxy/8S80//68vz/urTE/9XU5//j4+//9vX6////////////7e/u/+Dk5v/a4OT/8vf5///////g5+n/kZKe/25of/9sa3D/ZGNn/2pndf/ExdP/0tTf/+nm4//+/Pr//////87V2v+KmKf/cYOV/3mMn/+NnK3/TVll/1dYZP9kYHn/ODBG/yAcMP9fXW//ubzK/9PW3/9JREX/OTIu/7OxsP//////y9HZ/4WSov9xfo//TVhx/x8nNP8ZGiH/Y2F//3Bphv9vZYj/mZm3/7C1yv/Ey9n/DREX/wYIDf9ZaYb//v7+/8vS2v+Hk6T/h4+c/2hxg/8iJi//AwQE/0pHYP9nX4f/kIuv/4WIo/+GjaT/doOb/wECBv8dIS3/VWWL/w==
TEL;TYPE=work,voice;VALUE=uri:tel:+1-111-555-1212
TEL;TYPE=home,voice;VALUE=uri:tel:+1-404-555-1212
ADR;TYPE=work;LABEL="100 Waters Edge
Baytown,LA 30314
United States of America"
:;;100 Waters Edge;Baytown;LA;30314;United States of America
ADR;TYPE=home;LABEL="42 Plantation St.
Baytown,LA 30314
United States of America"
:;;42 Plantation St.;Baytown;LA;30314;United States of America
EMAIL:forrestgump@example.com
REV:20080424T195243Z
END:VCARD

View file

@ -0,0 +1,109 @@
'use strict';
var assert = require('assert');
var Contacts = require('./lib/contacts');
marionette('Contacts > Delete', function() {
var client = marionette.client({
profile: Contacts.config,
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
var subject;
var selectors;
setup(function() {
subject = new Contacts(client);
subject.launch();
selectors = Contacts.Selectors;
});
function goToSelectMode() {
// Click on settings button and wait for slide up animation
client.helper.waitForElement(selectors.settingsButton).click();
var settings = client.helper.waitForElement(selectors.settingsView);
subject.waitForSlideUp(settings);
// Click on delete button and wait for slide down animation
client.helper.waitForElement(selectors.bulkDelete).click();
subject.waitForSlideDown(settings);
}
function isDisabled(selector) {
return client.executeScript(function(selector) {
return document.querySelector(selector).disabled;
}, [selector]);
}
test(' > Edit menu is not visible on search mode', function() {
subject.addContact({
givenName: 'Hello',
tel: 1231231234
});
goToSelectMode();
var editForm = client.findElement(selectors.editForm);
editForm.displayed(function onDisplayed() {
// Enter in search mode
client.helper.waitForElement(selectors.searchLabel).click();
// Wait until the select-all view goes away
client.helper.waitForElementToDisappear(selectors.editMenu);
});
});
// Moztrap: https://moztrap.mozilla.org/manage/case/15181/
test(' > All contacts', function() {
subject.addContact({
givenName: 'Contact 1'
});
subject.addContact({
givenName: 'Contact 2'
});
goToSelectMode();
// Check that the action now is not available
assert.ok(isDisabled(selectors.selectAction));
// Click in select all
client.helper.waitForElement(selectors.selectAllButton).click();
// Click on delete
client.helper.waitForElement(selectors.selectAction).click();
// Click on the confirmation
client.helper.waitForElement(selectors.confirmDelete).click();
// Wait until the list appear and check it's empty
client.helper.waitForElement(selectors.noContacts);
// Wait for the status saying we removed contacts
var status = client.helper.waitForElement(selectors.multipleSelectStatus);
subject.waitForSlideDown(status);
client.executeScript(function() {
return !document.querySelector('#no-contacts').classList.contains('hide');
});
});
test('> Deleting last contact', function() {
subject.addContact({
givenName: 'Contact 1'
});
// Go to deatils
client.helper.waitForElement(selectors.listContactFirstText).click();
subject.waitSlideLeft('details');
// Get to the form
var edit = client.helper.waitForElement(selectors.detailsEditContact);
subject.clickOn(edit);
subject.waitForFadeIn(client.helper.waitForElement(selectors.form));
// Click on delete
client.helper.waitForElement(selectors.deleteContact).click();
client.helper.waitForElement(selectors.confirmDelete).click();
// Check the list has 0 contacts
subject.waitForFadeIn(client.helper.waitForElement(selectors.list));
assert.equal(subject.getNumberOfContacts(), 0);
});
});

View file

@ -0,0 +1,116 @@
'use strict';
var Contacts = require('./lib/contacts');
var ContactsData = require('./lib/contacts_data');
var assert = require('assert');
var ContactsListView = require('./lib/views/contact-list/view');
marionette('Contacts > Details', function() {
var client = marionette.client({
profile: Contacts.config,
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
var subject, actions;
var selectors;
var contactsData = new ContactsData(client);
var testContact;
setup(function() {
subject = new Contacts(client);
actions = client.loader.getActions();
subject.actions = actions;
subject.launch();
selectors = Contacts.Selectors;
testContact = {
tel: [{value: '1231231234', type: ['home']}],
givenName: ['Hello'],
familyName: ['World'],
name: ['Hello World'],
email: [{value: 'hello@example.com', type: ['home']}],
adr: [{type: ['home'], pref: true, streetAddress: 'False Street 123'}],
org: ['Mozilla'],
note: ['This is a note about the contact.']
};
});
function assertContactData(mozContact) {
var telNode = client.helper.waitForElement(selectors.detailsTelButtonFirst);
assert.equal(telNode.text(), mozContact.tel[0].value);
var nameNode = client.helper.waitForElement(selectors.detailsContactName);
assert.equal(nameNode.text(), mozContact.name[0]);
var emailNode = client.helper.waitForElement(selectors.detailsEmail);
assert.equal(emailNode.text(), mozContact.email[0].value);
var addressNode = client.helper.waitForElement(selectors.detailsAddress);
assert.equal(addressNode.text(), mozContact.adr[0].streetAddress);
var orgNode = client.helper.waitForElement(selectors.detailsOrg);
assert.equal(orgNode.text(), mozContact.org[0]);
var noteNode = client.helper.waitForElement(selectors.detailsNote);
assert.equal(noteNode.text(), mozContact.note[0]);
}
test('Regular contact is displayed correctly', function() {
contactsData.createMozContact(testContact);
client.helper.waitForElement(selectors.listContactFirstText).click();
subject.waitSlideLeft('details');
assertContactData(testContact);
});
test('Show contact with picture', function() {
contactsData.createMozContact(testContact, true);
client.helper.waitForElement(selectors.listContactFirstText).click();
subject.waitSlideLeft('details');
assertContactData(testContact);
var coverImg = client.helper.waitForElement(selectors.detailsCoverImage);
assert.ok(coverImg, 'Element should exist.');
client.waitFor(function() {
return coverImg.getAttribute('style').indexOf('background-image') != -1;
});
});
test('Share Contact', function() {
contactsData.createMozContact(testContact);
client.helper.waitForElement(selectors.listContactFirstText).click();
subject.waitSlideLeft('details');
// Click on share button
client.helper.waitForElement(selectors.detailsShareButton).click();
var sysMenu = subject.systemMenu;
// In the sysMenu they should appear at least the Messages and email apps
var menuOptions = sysMenu.findElements('button');
assert.ok(menuOptions.length >= 2);
});
suite('Favorite contact', function() {
setup(function(){
contactsData.createMozContact(testContact);
});
test('Mark contact as favorite', function() {
var contactsListView = new ContactsListView(client);
var contactDetailsView = contactsListView.goToContact();
contactDetailsView.makeFavorited();
assertContactData(testContact);
});
test('Favorite contact should appear in' +
' favorite and regular lists', function() {
var contactsListView = new ContactsListView(client);
var contactDetailsView = contactsListView.goToContact();
contactDetailsView.makeFavorited();
contactsListView = contactDetailsView.backtoContactsList();
assert.ok(contactsListView.firstContact.displayed());
assert.ok(contactsListView.firstFavorite.displayed());
});
});
});

View file

@ -0,0 +1,65 @@
'use strict';
var Contacts = require('./lib/contacts');
var assert = require('assert');
var fs = require('fs');
marionette('Export to SDCARD', function() {
var profile = Contacts.config;
// Enable device storage
var prefs = ['device.storage.enabled',
'device.storage.testing',
'device.storage.prompt.testing'];
prefs.forEach(pref => {
profile[pref] = true;
});
var client = marionette.client({
profile: profile,
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
var actions, subject, selectors;
setup(function() {
// Remove all files in temp device storage.
client.fileManager.removeAllFiles();
subject = new Contacts(client);
actions = client.loader.getActions();
subject.actions = actions;
subject.launch();
selectors = Contacts.Selectors;
});
test('> export one contact', function() {
// Create a contact first
subject.addContact({
givenName: 'John',
tel: 1231231234
});
// Go to settings
client.helper.waitForElement(selectors.settingsButton).click();
// Now click on import contacts button
client.helper.waitForElement(selectors.exportButton).click();
client.helper.waitForElement(selectors.exportSDCard).click();
// Select all and export
client.helper.waitForElement(selectors.selectAllButton).click();
client.helper.waitForElement(selectors.selectAction).click();
// Check the string on the status
var status = client.helper.waitForElement(selectors.multipleSelectStatus);
assert.equal('1/1 contacts exported', status.text());
// Check we have a file in the sdcard
var path = client.fileManager.deviceStorage.getDeviceStoragePath();
console.log('path is ' + path);
assert.ok(fs.existsSync(path));
var files = fs.readdirSync(path);
assert.equal(files.length, 1);
subject.waitForFadeIn(client.helper.waitForElement(selectors.list));
});
});

View file

@ -0,0 +1,390 @@
'use strict';
var Contacts = require('./lib/contacts'),
ContactsData = require('./lib/contacts_data'),
Dialer = require('../../../dialer/test/marionette/lib/dialer'),
assert = require('assert'),
/*jshint -W079 */
System = require('../../../../system/test/marionette/lib/system');
// XXX Bug 1139799 - Enable test for Bug 1116889
// Gallery = require('../../../../gallery/test/marionette/lib/gallery');
marionette('Contacts > Form', function() {
var client = marionette.client({
profile: Contacts.config,
desiredCapabilities: { raisesAccessibilityExceptions: false }
}),
subject,
contactsData,
dialerSubject,
dialerSelectors,
selectors,
system;
setup(function() {
subject = new Contacts(client);
contactsData = new ContactsData(client);
selectors = Contacts.Selectors;
dialerSubject = new Dialer(client);
dialerSelectors = Dialer.Selectors;
subject.launch();
system = new System(client);
});
teardown(function() {
subject = null;
contactsData = null;
selectors = null;
dialerSubject = null;
dialerSelectors = null;
system = null;
});
var contactData = {
givenName: ['Jose'],
familyName: ['Cantera'],
org: ['EDM'],
tel: [{
type: ['mobile'],
value: '637654321'
}]
};
function editFirstContact() {
var firstContact = client.helper.waitForElement(selectors.listContactFirst);
subject.clickOn(firstContact);
subject.waitSlideLeft('details');
var edit = client.helper.waitForElement(selectors.detailsEditContact);
subject.clickOn(edit);
subject.waitForFadeIn(client.helper.waitForElement(selectors.form));
}
function editContactPhoto() {
editFirstContact();
var photoChangeButton = client.helper.waitForElement(
selectors.formPhotoButton);
subject.clickOn(photoChangeButton);
}
suite('> Add Contact', function() {
test('Add a simple contact', function() {
var givenName = 'Hello';
var familyName = 'World';
subject.addContact({
givenName: givenName,
familyName: familyName
});
var listView = client.helper.waitForElement(selectors.list);
assert.ok(listView.displayed(), 'List view is shown.');
var listElementText = client.helper.waitForElement(
selectors.listContactFirst).text();
assert.notEqual(listElementText.indexOf(givenName), -1);
assert.notEqual(listElementText.indexOf(familyName), -1);
});
});
suite('> Edit Contact', function() {
test('Simple edition of contact details. ORG field', function() {
contactsData.createMozContact(contactData);
editFirstContact();
client.helper.waitForElement(selectors.formOrg).sendKeys('v2');
client.helper.waitForElement(selectors.formSave).click();
subject.waitForFadeIn(client.helper.waitForElement(selectors.details));
var edit = client.helper.waitForElement(selectors.detailsEditContact);
subject.clickOn(edit);
subject.waitForFadeIn(client.helper.waitForElement(selectors.form));
var orgField = client.helper.waitForElement(selectors.formOrg);
var orgFieldValue = orgField.getAttribute('value');
assert.ok(orgFieldValue.trim() === 'v2EDM');
});
test('Can create custom label', function() {
contactsData.createMozContact(contactData);
editFirstContact();
client.helper.waitForElement(selectors.formTelLabelFirst).click();
subject.waitSlideLeft('formCustomTagPage');
client.helper.waitForElement(selectors.formCustomTag).sendKeys('BFF');
client.helper.waitForElement(selectors.formCustomTagDone).click();
// Wait for the custom tag page to disappear
var bodyWidth = client.findElement(selectors.body).size().width;
client.waitFor(function waiting() {
var tagPage = client.findElement(selectors.formCustomTagPage);
var location = tagPage.location();
return location.x >= bodyWidth;
});
client.findElement(selectors.formSave).click();
subject.waitForFadeIn(client.helper.waitForElement(selectors.details));
client.helper.waitForElement(selectors.detailsTelLabelFirst);
client.waitFor(function waiting() {
var label = client.helper.
waitForElement(selectors.detailsTelLabelFirst);
return label.text() === 'BFF';
});
assert.ok(true, 'custom label is updated.');
});
suite('> Edit Contact Photo', function() {
setup(function() {
// We create a mozContact with a photo
contactsData.createMozContact(contactData, true);
});
function areRemoveAndChangePresent() {
return client.executeScript(function(selector) {
var buttons = document.querySelectorAll(selector);
var out = 0;
for(var j = 0; j < buttons.length; j++) {
if (buttons[j].textContent === 'Remove photo') {
out++;
}
if (buttons[j].textContent === 'Change photo') {
out++;
}
}
return out === 2;
}, ['#value-menu button']);
}
test('Edit regular Contact with image', function() {
editContactPhoto();
client.helper.waitForElement(selectors.actionMenu);
assert.ok(areRemoveAndChangePresent());
});
});
suite('> Adding and removing', function() {
test('Template ids unique', function() {
var data = {
givenName: ['John'],
familyName: ['Doe'],
tel: [{
type: ['mobile'],
value: '1111111'
}, {
type: ['mobile'],
value: '222222'
}]
};
contactsData.createMozContact(data);
editFirstContact();
// Delete the first phone and click on add a new phone
client.helper.waitForElement(selectors.formDelFirstTel).click();
client.helper.waitForElement(selectors.formAddNewTel).click();
client.helper.waitForElement(selectors.formSave).click();
subject.waitForFadeIn(client.helper.waitForElement(selectors.details));
var phoneList = client.findElements(selectors.formTel);
assert.equal(phoneList.length, 2);
assert.equal(phoneList[1].getAttribute('id'), 'number_2');
});
});
/* XXX Bug 1139799 - Enable test for Bug 1116889
suite('> Discard edited contact photo', function() {
setup(function() {
// Create a contact without photo
contactsData.createMozContact(contactData, false);
client.switchToFrame();
// This test makes use of gallery images, so we need to populate
// the pictures folder with test images and launch the gallery app
// to make sure that it scans the images properly.
client.fileManager.add({
type: 'pictures',
filePath: 'test_media/Pictures/firefoxOS.png'
});
var gallery = new Gallery(client);
gallery.launch();
client.apps.close(Gallery.ORIGIN);
client.switchToFrame();
subject.launch();
});
test('Select photo and discard it', function() {
// Select first contact and click on the edit button.
editFirstContact();
// Check that we have no photo for this contact.
var photoBeforeEdit =
client.helper.waitForElement(selectors.formPhotoImg);
assert.ok(photoBeforeEdit.getAttribute('style') === '');
// Click on the photo button to edit it. This triggers an activity
// selection menu.
var photoChangeButton =
client.helper.waitForElement(selectors.formPhotoButton);
subject.clickOn(photoChangeButton);
// Switch to system frame.
client.switchToFrame();
// Select gallery option on activity selector.
var gallery = system.getActivityOptionMatching('gallery');
subject.clickOn(gallery);
// Choose an image.
client.apps.switchToApp('app://gallery.gaiamobile.org');
var thumbnail = client.helper.waitForElement(selectors.galleryImage);
subject.clickOn(thumbnail);
var done =
client.helper.waitForElement(selectors.galleryDone);
subject.clickOn(done);
// Get back to contacts app.
client.switchToFrame();
client.apps.switchToApp(Contacts.URL, 'contacts');
// Discard changes.
var formHeader =
client.helper.waitForElement(selectors.formHeader);
formHeader.tap(10, 10);
subject.waitSlideLeft('details');
// Edit again.
var edit =
client.helper.waitForElement(selectors.detailsEditContact);
subject.clickOn(edit);
client.helper.waitForElement(selectors.form);
// Check that we have no photo after discarding the selection.
var photoAfterEdit =
client.helper.waitForElement(selectors.formPhotoImg);
assert.ok(photoAfterEdit.getAttribute('style') === '');
});
});
*/
});
suite('> Facebook contacts', function() {
var fbContactData;
setup(function() {
fbContactData = contactsData.createFbContact();
});
function isGalleryButtonPresent() {
return client.executeScript(function(selector) {
var buttons = document.querySelectorAll(selector);
var out = false;
for(var j = 0; j < buttons.length; j++) {
if (buttons[j].textContent === 'Gallery') {
out = true;
}
}
return out;
}, [selectors.buttonActivityChooser]);
}
test('Add phone number from Dialer to existing Facebook contact',
function() {
client.apps.close(Contacts.URL, 'contacts');
dialerSubject.launch();
var one = client.findElement(dialerSelectors.one),
two = client.findElement(dialerSelectors.two),
three = client.findElement(dialerSelectors.three);
for (var i = 0; i < 3; i++) {
one.tap();
two.tap();
three.tap();
}
var phoneNumber = dialerSubject.client.findElement(
dialerSelectors.phoneNumber);
client.waitFor(function() {
return (phoneNumber.getAttribute('value').length === 9);
});
var addContact = dialerSubject.client.findElement(
dialerSelectors.keypadCallBarAddContact);
addContact.tap();
var addToExistingContact = dialerSubject.client.helper.waitForElement(
dialerSelectors.addToExistingContactMenuItem);
addToExistingContact.tap();
client.switchToFrame();
client.apps.switchToApp(Contacts.URL, 'contacts');
client.findElement(selectors.listContactFirst).tap();
subject.waitForFormShown();
var formTelNumberSecond = client.helper.waitForElement(
selectors.formTelNumberSecond);
var formEmailFirst = client.helper.waitForElement(
selectors.formEmailFirst);
assert.equal(formTelNumberSecond.getAttribute('value'),
fbContactData.tel[0].value);
assert.equal(formEmailFirst.getAttribute('value'),
fbContactData.email[0].value);
});
test('Contact Photo cannot be removed', function() {
editContactPhoto();
// As it is a Facebook contact it should appear the activity window
// to choose the source for the image
client.switchToFrame();
client.helper.waitForElement(selectors.activityChooser);
assert.ok(isGalleryButtonPresent());
});
}); // Facebook Contacts
suite('> Input buttons', function() {
function assertClear(inputSelector) {
var input = client.helper.waitForElement(inputSelector);
input.tap();
input.sendKeys('1234');
client.findElement(inputSelector + ' + button[type="reset"]').tap();
assert.strictEqual(input.getAttribute('value'), '');
}
test('Clear buttons work as expected', function() {
contactsData.createMozContact(contactData);
editFirstContact();
var fieldSelectors = [
selectors.formGivenName,
selectors.formTelNumberFirst,
selectors.formFamilyName,
selectors.formOrg,
selectors.formEmailFirst
];
for (var i = 0, len = fieldSelectors.length; i < len; i++){
assertClear(fieldSelectors[i]);
}
});
});
});

View file

@ -0,0 +1,563 @@
'use strict';
var Contacts = require('./lib/contacts');
var ContactsData = require('./lib/contacts_data');
var assert = require('assert');
marionette('Contacts > ICE contacts', function() {
var client = marionette.client({
profile: Contacts.config,
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
var contactsData = new ContactsData(client);
var actions, subject, selectors;
var testContactTelNumber = '655555555';
var testContact = {
givenName: ['Hello'],
tel: [{
type: ['mobile'],
value: testContactTelNumber
}]
};
setup(function() {
subject = new Contacts(client);
actions = client.loader.getActions();
subject.actions = actions;
subject.launch();
selectors = Contacts.Selectors;
});
function setFirstContactAsICE() {
openICESettings();
clickICESwitch(1);
var listContactFirstText =
client.helper.waitForElement(selectors.listContactFirstText);
subject.clickOn(listContactFirstText);
}
function clickICESwitch(switchNumber){
var iceSwitch =
client.helper.waitForElement(selectors['iceSwitch' + switchNumber]);
subject.clickOn(iceSwitch);
var iceButton =
client.helper.waitForElement(selectors['iceButton' + switchNumber]);
subject.clickOn(iceButton);
}
function openICESettings() {
var settingsBtn = client.helper.waitForElement(selectors.settingsButton);
subject.clickOn(settingsBtn);
var settings = client.helper.waitForElement(selectors.settingsView);
subject.waitForFadeIn(settings);
client.helper.waitForElement(selectors.setIceButton).click();
}
function addTestContact(addTelephone) {
var tel = addTelephone ? testContactTelNumber : undefined;
subject.addContact({
givenName: 'Hello',
tel: tel
});
}
function clickBackArrow() {
var contactsHeader =
client.helper.waitForElement(selectors.iceSettingsHeader);
actions.wait(0.5).tap(contactsHeader, 10, 10).perform();
}
function isIceContactsGroupHidden() {
return client.executeScript(function(selector) {
return document.querySelector(selector).classList.contains('hide');
}, [selectors.iceGroupOpen]);
}
function assertICEContact(position, name) {
var iceContact = client.helper.waitForElement(
selectors['iceButton' + position]);
assert.equal(iceContact.text(), name);
var checked = client.executeScript(function(selector) {
return document.querySelector(selector).checked;
}, [selectors['iceInputSwitch' + position]]);
assert.ok(checked);
}
function assertICEContactNotSet(position) {
var checked = client.executeScript(function(selector) {
return document.querySelector(selector).checked;
}, [selectors['iceInputSwitch' + position]]);
assert.ok(!checked);
}
// Creates a mozContact by calling the mozContacts API
function createMozContact(contactData) {
return contactsData.createMozContact(contactData);
}
function setICEContacts(iceContactsList) {
var doSetICEContacts = function(iceContactsList) {
var LazyLoader = window.wrappedJSObject.LazyLoader;
LazyLoader.load([
'/shared/js/contacts/utilities/ice_store.js',
'/contacts/js/utilities/ice_data.js',
'/shared/js/async_storage.js'
], function() {
var ICEData = window.wrappedJSObject.ICEData;
ICEData.setICEContacts(iceContactsList).then(function() {
marionetteScriptFinished(true);
}, function(error) {
console.error('Error while setting ICE ', error);
marionetteScriptFinished(false);
});
});
};
var iceContactsSet = false;
client.executeAsyncScript(doSetICEContacts, [iceContactsList],
function(err, val) {
iceContactsSet = val;
});
client.waitFor(function() {
return iceContactsSet;
});
}
function setICEContact(position, contact) {
var contactId = createMozContact(contact || testContact);
var iceContacts = [null, null];
iceContacts[position - 1] = contactId;
setICEContacts(iceContacts);
}
function createAndSetICEContacts(contactList) {
var iceList = [];
contactList.forEach(function(aContact) {
var contactId = createMozContact(aContact);
iceList.push(contactId);
});
setICEContacts(iceList);
}
suite('ICE settings', function() {
test('Check ICE settings transition', function() {
addTestContact();
openICESettings();
clickICESwitch(1);
clickBackArrow();
clickICESwitch(2);
clickBackArrow();
});
test('Clicking on ICE button does not toggle the switch', function() {
addTestContact();
openICESettings();
var iceButton1 = client.helper.waitForElement(selectors.iceButton1);
subject.clickOn(iceButton1);
var checked = client.executeScript(function(selector) {
return document.querySelector(selector).checked;
}, [selectors.iceInputSwitch1]);
assert.ok(!checked);
});
test('Select ICE contact from search, go back to ICE settings', function() {
addTestContact(true);
openICESettings();
clickICESwitch(1);
var searchLabel = client.helper.waitForElement(selectors.searchLabel);
subject.clickOn(searchLabel);
// We search for the first letter of givenName in addTestContact()
client.helper.waitForElement(selectors.searchInput).sendKeys('H');
client.helper.waitForElement(selectors.searchResultFirst).click();
clickBackArrow();
var settingsClose = client.helper.waitForElement(selectors.settingsClose);
subject.clickOn(settingsClose);
var openIce = client.helper.waitForElement(selectors.iceGroupOpen);
subject.clickOn(openIce);
var iceContact = client.helper.waitForElement(selectors.iceContact);
// 'Hello' is givenName in addTestContact()
assert.ok(iceContact.text().indexOf('Hello') >= 0,
'The name of the contact should appear as ICE contact');
});
test('ICE contacts can\'t be repeated', function() {
addTestContact(true);
setFirstContactAsICE();
clickICESwitch(2);
var listContactFirstText =
client.helper.waitForElement(selectors.listContactFirstText);
subject.clickOn(listContactFirstText);
var confirmText = client.helper.waitForElement(selectors.confirmBody)
.text();
var expectedResult = subject.l10n(
'/locales-obj/contacts.index.en-US.json',
'ICERepeatedContact');
assert.equal(confirmText, expectedResult);
});
test('Contact must have a phone number', function() {
addTestContact(false); // Added contact without phone
setFirstContactAsICE();
var confirmText = client.helper.waitForElement(selectors.confirmBody)
.text();
var expectedResult = subject.l10n(
'/locales-obj/contacts.index.en-US.json',
'ICEContactNoNumber');
assert.equal(confirmText, expectedResult);
});
});
suite('ICE contacts edition', function() {
function dismissAndGoBack() {
client.helper.waitForElement(selectors.confirmDismiss).click();
var header = client.helper.waitForElement(selectors.detailsHeader);
actions.wait(0.5).tap(header, 10, 10).perform();
}
function deleteFirstContactFirstTel() {
var firstContact =
client.helper.waitForElement(selectors.listContactFirst);
subject.clickOn(firstContact);
var edit = client.helper.waitForElement(selectors.detailsEditContact);
subject.clickOn(edit);
subject.waitForFadeIn(client.helper.waitForElement(selectors.form));
client.findElement(selectors.formDelFirstTel).click();
var save = client.findElement(selectors.formSave);
save.enabled();
save.click();
}
function closeSettings() {
var close = client.helper.waitForElement(selectors.settingsClose);
subject.clickOn(close);
}
suite('With one phone number', function() {
setup(function() {
addTestContact(true);
setFirstContactAsICE();
clickBackArrow();
closeSettings();
deleteFirstContactFirstTel();
});
test('Confirm window appears with correct message', function() {
var confirmBody = client.helper.waitForElement(selectors.confirmBody);
var expectedResult = subject.l10n(
'/locales-obj/contacts.index.en-US.json',
'ICEContactDelTelAll');
assert.equal(confirmBody.text(), expectedResult);
});
test('Contact is deleted from ICE when no numbers left', function() {
dismissAndGoBack();
openICESettings();
var iceButton1 = client.helper.waitForElement(selectors.iceButton1);
assert.ok(!iceButton1.enabled());
});
test('ICE list empty after removing phone', function() {
dismissAndGoBack();
client.helper.waitForElement(selectors.contactListHeader);
assert.ok(isIceContactsGroupHidden());
});
});
suite('With more than one phone number', function() {
test('Deleting the first number keeps the contact as ICE', function() {
var mozContact = {
tel: [
{
value: '01234',
type: ['home']
},
{
value: '56789',
type:['home']
}
],
givenName: ['Hello'],
familyName: ['World'],
name: ['Hello World']
};
createMozContact(mozContact);
setFirstContactAsICE();
clickBackArrow();
closeSettings();
deleteFirstContactFirstTel();
dismissAndGoBack();
subject.waitForFadeIn(client.helper.waitForElement(selectors.list));
openICESettings();
assertICEContact(1, mozContact.name[0]);
});
});
});
suite('ICE contacts and Merge', function() {
// Merges a duplicate contact and closes the duplicates iframe
function mergeDuplicate() {
var duplicateFrame = client.findElement(selectors.duplicateFrame);
subject.waitForSlideUp(duplicateFrame);
client.switchToFrame(duplicateFrame);
var mergeAction = client.helper.waitForElement(selectors.duplicateMerge);
subject.clickOn(mergeAction);
client.switchToFrame();
client.apps.switchToApp(Contacts.URL, 'contacts');
subject.waitForSlideDown(duplicateFrame);
}
function setTwoICEDuplicates() {
var duplicateContact = testContact;
var contactId1 = createMozContact(duplicateContact);
var contactId2 = createMozContact(duplicateContact);
setICEContacts([contactId1, contactId2]);
}
function addNewAndMatchTest(position) {
addTestContact(true);
mergeDuplicate();
// Now we go back to the ICE settings and check that our ICE remains
subject.waitForFadeIn(client.helper.waitForElement(selectors.list));
openICESettings();
assertICEContact(position, 'Hello');
}
function findDuplicatesAndMatchTest(position) {
var secondContactID = createMozContact({
givenName: ['Hello'],
tel: [{
type: ['mobile'],
value: testContactTelNumber
}]
});
var selectorForSecondContact = 'li[data-uuid="' + secondContactID + '"]';
// Going to the recently created contact
var listContactElement =
client.helper.waitForElement(selectorForSecondContact);
subject.clickOn(listContactElement);
subject.waitSlideLeft('details');
var findDups = client.helper.waitForElement(selectors.findDupsButton);
subject.clickOn(findDups);
mergeDuplicate();
subject.backToList();
openICESettings();
assertICEContact(position, 'Hello');
}
function editAndMatchTest(position) {
subject.addContact({
givenName: 'Good Bye',
tel: '+34638883074'
});
var selectorForSecondContact = 'li[data-group="G"].contact-item';
// Going to the recently created contact
var listContactElement =
client.helper.waitForElement(selectorForSecondContact);
subject.clickOn(listContactElement);
// Editing contact
subject.editContact();
// Adding a tel number equal to the tel number of 'Hello'
client.findElement(selectors.formAddNewTel).click();
client.helper.waitForElement(selectors.formTelNumberSecond).
sendKeys(testContactTelNumber);
// Saving the contact
var save = client.findElement(selectors.formSave);
save.enabled();
save.click();
mergeDuplicate();
subject.backToList();
openICESettings();
assertICEContact(position, 'Good Bye');
}
suite('> 1 ICE Contact. Position 1', function() {
setup(function() {
setICEContact(1);
});
test('> Add a new Contact that matches ICE Contact', function() {
addNewAndMatchTest(1);
assertICEContactNotSet(2);
});
test('> Find duplicates. ICE Contact Matches', function() {
findDuplicatesAndMatchTest(1);
assertICEContactNotSet(2);
});
test('> Edit existing, change data. ICE Matches. ICE changed',
function() {
editAndMatchTest(1);
assertICEContactNotSet(2);
});
});
suite('> 1 ICE Contact. Position 2', function() {
setup(function() {
setICEContact(2);
});
test('> Add a new Contact that matches ICE Contact', function() {
addNewAndMatchTest(2);
assertICEContactNotSet(1);
});
test('> Find duplicates. ICE Contact Matches', function() {
findDuplicatesAndMatchTest(2);
assertICEContactNotSet(1);
});
test('> Edit existing, change data. ICE Matches. ICE changed',
function() {
editAndMatchTest(2);
assertICEContactNotSet(1);
});
});
suite('> 2 ICE Contacts. Change position 1. Keep Position 2', function() {
setup(function() {
createAndSetICEContacts([testContact, {
givenName: ['Aufwiedersehen'],
tel: [{
type: ['mobile'],
value: '9999999'
}]
}]);
});
test('> Add a new Contact that matches ICE Contact', function() {
addNewAndMatchTest(1);
assertICEContact(2, 'Aufwiedersehen');
});
test('> Find duplicates. ICE Contact Matches', function() {
findDuplicatesAndMatchTest(1);
assertICEContact(2, 'Aufwiedersehen');
});
test('> Edit existing, change data. ICE Matches. ICE changed',
function() {
editAndMatchTest(1);
assertICEContact(2, 'Aufwiedersehen');
});
});
suite('> 2 ICE Contacts. Change position 2. Keep Position 1', function() {
setup(function() {
createAndSetICEContacts([{
givenName: ['Aufwiedersehen'],
tel: [{
type: ['mobile'],
value: '9999999'
}]
}, testContact]);
});
test('> Add a new Contact that matches ICE Contact', function() {
addNewAndMatchTest(2);
assertICEContact(1, 'Aufwiedersehen');
});
test('> Find duplicates. ICE Contact Matches', function() {
findDuplicatesAndMatchTest(2);
assertICEContact(1, 'Aufwiedersehen');
});
test('> Edit existing, change data. ICE Matches. ICE changed',
function() {
editAndMatchTest(2);
assertICEContact(1, 'Aufwiedersehen');
});
});
suite('> 2 ICE Contacts which are merged', function() {
setup(function() {
setTwoICEDuplicates();
});
test('> Two ICE Contacts are merged into 1', function() {
// Going to the recently created contact
var listContactElement =
client.helper.waitForElement(selectors.listContactFirst);
subject.clickOn(listContactElement);
subject.waitSlideLeft('details');
var findDups = client.helper.waitForElement(selectors.findDupsButton);
subject.clickOn(findDups);
mergeDuplicate();
subject.backToList();
openICESettings();
assertICEContact(1, 'Hello');
assertICEContactNotSet(2);
});
test('> A third incoming Contact merges two ICE contacts', function() {
addTestContact(true);
mergeDuplicate();
// Now we go back to the ICE settings and check that our ICE remains
subject.waitForFadeIn(client.helper.waitForElement(selectors.list));
openICESettings();
assertICEContact(1, 'Hello');
assertICEContactNotSet(2);
});
});
});
});

View file

@ -0,0 +1,55 @@
'use strict';
var Contacts = require('./lib/contacts');
var assert = require('assert');
marionette('Import from SDCARD', function() {
var profile = Contacts.config;
// Enable device storage
var prefs = ['device.storage.enabled',
'device.storage.testing',
'device.storage.prompt.testing'];
prefs.forEach(pref => {
profile[pref] = true;
});
var client = marionette.client({
profile: profile,
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
var contactsApp, selectors;
setup(function() {
client.fileManager.removeAllFiles();
client.fileManager.add({
type: 'text',
filePath: 'apps/communications/contacts/test/marionette/data/vcard_4.vcf'
});
contactsApp = new Contacts(client);
contactsApp.launch();
selectors = Contacts.Selectors;
});
test('> one contact in one file', function() {
// Go to settings
client.helper.waitForElement(selectors.settingsButton).tap();
// Now click on import contacts button
client.helper.waitForElement(selectors.importContacts).tap();
client.helper.waitForElement(selectors.importSDCard).tap();
// Get back to the list
client.switchToShadowRoot(client.helper
.waitForElement(selectors.importHeader));
contactsApp.clickOn(client.helper.waitForElement(selectors.actionButton));
client.switchToShadowRoot();
contactsApp.waitForFadeIn(client.helper
.waitForElement(selectors.settingsView));
client.helper.waitForElement(selectors.settingsClose).tap();
contactsApp.waitForFadeIn(client.helper.waitForElement(selectors.list));
// Check that we have 1 contact and is the one from the SD,
var firstContact = client.findElement(selectors.listContactFirstText);
assert.equal(firstContact.text(), 'Forrest Gump');
});
});

View file

@ -0,0 +1,477 @@
'use strict';
/**
* Abstraction around contacts app.
* @constructor
* @param {Marionette.Client} client for operations.
*/
function Contacts(client) {
this.client = client;
this.client.setSearchTimeout(10000);
this.actions = this.client.loader.getActions();
}
/**
* @type String Origin of contacts app
*/
Contacts.URL = 'app://communications.gaiamobile.org';
Contacts.config = {
prefs: {
'device.storage.enabled': true,
'device.storage.testing': true,
'device.storage.prompt.testing': true
}
};
Contacts.Selectors = {
body: 'body',
bodyReady: 'body .view-body',
settingsButton: '#settings-button',
confirmHeader: '#confirmation-message h1',
confirmBody: '#confirmation-message p',
confirmDismiss: '#confirmation-message menu button',
confirmDelete: '#confirmation-message .danger',
details: '#view-contact-details',
detailsEditContact: '#edit-contact-button',
detailsTelLabelFirst: '#phone-details-template-0 h2',
detailsTelButtonFirst: '.button.icon-call[data-tel]',
detailsEmail: '#contact-detail-inner #email-details-template-0 div.item',
detailsAddress: '#contact-detail-inner #address-details-template-0 div.item',
detailsOrg: '#contact-detail-inner #org-title',
detailsNote: '#contact-detail-inner #note-details-template-0',
detailsFindDuplicate: '#contact-detail-inner #find-merge-button',
detailsFavoriteButton: '#toggle-favorite',
detailsContactName: '#contact-name-title',
detailsHeader: '#details-view-header',
detailsSocialLabel: '#contact-detail-inner #details-list #social-label',
detailsSocialTemplate: '#contact-detail-inner #details-list .social-actions',
detailsCoverImage: '#cover-img',
detailsShareButton: '#contact-detail-inner #share_button',
findDupsButton: '#details-list #find-merge-button',
duplicateFrame: 'iframe[src*="matching_contacts.html"]',
duplicateHeader: '#title',
duplicateClose: '#merge-close',
duplicateMerge: '#merge-action',
exportButton: '#exportContacts button',
exportSDCard: '#export-sd-option button',
form: '#view-contact-form',
formTitle: '#contact-form-title',
formCustomTag: '#custom-tag',
formCustomTagPage: '#view-select-tag',
formCustomTagDone: '#view-select-tag #settings-done',
formNew: '#add-contact-button',
formGivenName: '#givenName',
formOrg: '#org',
formFamilyName: '#familyName',
formSave: '#save-button',
formTel: '#contacts-form-phones input[type="tel"]',
formDelFirstTel: '#add-phone-0 .img-delete-button',
formTelLabelFirst: '#tel_type_0',
formTelNumberFirst: '#number_0',
formTelNumberSecond: '#number_1',
formEmailFirst: '#email_0',
formEmailSecond: '#email_1',
formPhotoButton: '#photo-button',
formAddNewTel: '#add-new-phone',
formAddNewEmail: '#add-new-email',
formHeader: '#contact-form-header',
formPhotoImg: '#thumbnail-photo',
deleteContact: '#delete-contact',
groupList: ' #groups-list',
contacts: '#groups-list .contact-item',
list: '#view-contacts-list',
listContactFirst: 'li:not([data-group="ice"]).contact-item',
listContactFirstText: 'li:not([data-group="ice"]).contact-item p',
contactsItems: 'li:not([data-group="ice"]).contact-item p',
contactListHeader: '#contacts-list-header',
contactsIframe: '#iframe-contacts',
searchLabel: '#search-start',
searchInput: '#search-contact',
searchCancel: '#cancel-search',
searchResultFirst: '#search-list .contact-item',
scrollbar: 'nav[data-type="scrollbar"]',
overlay: 'nav[data-type="scrollbar"] p',
settingsView: '#view-settings',
settingsClose: '#settings-close',
bulkDelete: '#bulkDelete',
orderSwitch: '#settingsOrder gaia-switch',
changeOrder: '#switch',
editForm: '#selectable-form',
editMenu: '#select-all-wrapper',
selectAction: '#select-action',
selectAllButton: '#select-all',
clearOrgButton: '#clear-org',
setIceButton: '#set-ice',
iceHeader: '#ice-header',
iceSettingsHeader: '#ice-settings-header',
iceSettings: '#ice-settings',
iceSwitch1: '#ice-contacts-1-switch',
iceInputSwitch1: '#ice-contacts-1-switch input[type="checkbox"]',
iceSwitch2: '#ice-contacts-2-switch',
iceInputSwitch2: '#ice-contacts-2-switch input[type="checkbox"]',
iceButton1: '#select-ice-contact-1',
iceButton2: '#select-ice-contact-2',
iceGroupOpen: '#section-group-ice',
iceContact: '#ice-group .contact-item',
importContacts: '#importContacts',
importSDCard: '#import-sd-option button',
importHeader: '#import-settings-header',
activityChooser: 'form[data-type="action"]',
buttonActivityChooser: 'form[data-type="action"] button',
actionMenu: '#action-menu',
actionMenuList: '#value-menu',
actionButton: '.action-button',
multipleSelectSave: '#save-button',
multipleSelectStatus: '#statusMsg p',
multipleSelectList: '#multiple-select-container',
systemMenu: 'form[data-z-index-level="action-menu"]',
galleryImage: '.thumbnail img',
galleryDone: '#crop-done-button',
header: '#edit-title',
noContacts: '#no-contacts'
};
Contacts.prototype = {
/**
* Launches contacts app and focuses on frame.
*/
launch: function() {
this.client.apps.launch(Contacts.URL, 'contacts');
this.switchToApp();
this.client.helper.waitForElement(Contacts.Selectors.bodyReady);
},
relaunch: function() {
this.close();
this.launch();
},
close: function() {
this.client.apps.close(Contacts.URL, 'contacts');
},
switchToApp: function() {
this.client.switchToFrame();
// switchToApp already waits for the app to be displayed
this.client.apps.switchToApp(Contacts.URL, 'contacts');
},
switchToCreateNewContactActivity: function() {
this._switchToActivity('views/form/form.html');
},
switchToMergeInterface: function() {
this._switchToActivity('views/matching/matching_contacts.html');
},
_switchToActivity: function(href) {
this.client.switchToFrame();
// switchToActivity already waits for the app to be displayed
this.client.apps.switchToActivity(Contacts.URL, '/contacts/' + href);
},
/**
* Returns a localized string from a properties file.
* @param {String} file to open.
* @param {String} key of the string to lookup.
*/
l10n: function(file, key) {
var ast = this.client.executeScript(function(file, key) {
var xhr = new XMLHttpRequest();
var data;
xhr.open('GET', file, false); // Intentional sync
xhr.onload = function(o) {
data = JSON.parse(xhr.response);
};
xhr.send(null);
return data;
}, [file, key]);
for (var i = 0; i < ast.length; i++) {
if (ast[i].$i === key) {
return ast[i].$v;
}
}
},
waitSlideLeft: function(elementKey) {
var element = this.client.findElement(Contacts.Selectors[elementKey]),
location;
var test = function() {
location = element.location();
return location.x <= 0;
};
this.client.waitFor(test);
},
waitForSlideDown: function(element) {
var bodyHeight = this.client.findElement(Contacts.Selectors.body).
size().height;
var test = function() {
return element.location().y >= bodyHeight;
};
this.client.waitFor(test);
},
waitForSlideUp: function(element) {
var test = function() {
return element.location().y <= 0;
};
this.client.waitFor(test);
},
waitForFadeIn: function(element) {
var test = function() {
var opacity = element.cssProperty('opacity');
var pointerEvents = element.cssProperty('pointer-events');
return opacity == 1 && pointerEvents == 'auto';
};
this.client.waitFor(test);
},
waitForFormShown: function() {
var form = this.client.helper.waitForElement(Contacts.Selectors.form),
location;
var test = function() {
location = form.location();
return location.y <= 0;
};
this.client.waitFor(test);
},
waitForFormTransition: function() {
var selectors = Contacts.Selectors;
var form = this.client.findElement(selectors.form);
try {
this.client.helper.waitForElementToDisappear(form);
} catch (e) {
// When the contact details pages was created in an activity and we close
// it, we might encounter errors like "GenericError: can't access dead
// object" or 'JavaScriptError: unload was called'. So we drop them as it
// makes the tests crash.
var isKnownError = e.message.indexOf(
'GenericError: can\'t access dead object') > -1 ||
e.message.indexOf('JavaScriptError: unload was called') > -1;
if (!isKnownError) {
throw e;
}
}
},
editContact: function() {
var selectors = Contacts.Selectors;
var edit = this.client.helper.waitForElement(selectors.detailsEditContact);
this.clickOn(edit);
this.waitForFadeIn(this.client.helper.waitForElement(selectors.form));
},
// Goes back to the Contact List view from a Details view
backToList: function() {
var selectors = Contacts.Selectors;
// Now we go back to the ICE settings and check that our ICE remains
this.waitForFadeIn(this.client.helper.waitForElement(selectors.details));
var header = this.client.helper.waitForElement(selectors.detailsHeader);
this.actions.wait(0.5).tap(header, 10, 10).perform();
this.waitSlideLeft('list');
},
enterContactDetails: function(details) {
var selectors = Contacts.Selectors;
details = details || {
givenName: 'Hello',
familyName: 'Contact',
org: 'Enterprise'
};
this.waitForFormShown();
for (var i in details) {
// Camelcase details to match form.* selectors.
var key = 'form' + i.charAt(0).toUpperCase() + i.slice(1);
this.client.findElement(selectors[key])
.sendKeys(details[i]);
}
this.client.findElement(selectors.formSave).click();
},
addContact: function(details) {
var selectors = Contacts.Selectors;
var addContact = this.client.findElement(selectors.formNew);
addContact.click();
this.enterContactDetails(details);
this.waitForFormTransition();
this.client.helper.waitForElement(selectors.list);
},
mergeContact: function(details) {
var selectors = Contacts.Selectors;
var addContact = this.client.findElement(selectors.formNew);
addContact.click();
this.enterContactDetails(details);
this.switchToMergeInterface();
var duplicateFrame = this.client.findElement(selectors.duplicateFrame);
var mergeAction =
this.client.helper.waitForElement(selectors.duplicateMerge);
this.clickOn(mergeAction);
this.switchTo();
this.waitForSlideDown(duplicateFrame);
},
addContactMultipleEmails: function(details) {
var selectors = Contacts.Selectors;
var addContact = this.client.findElement(selectors.formNew);
addContact.click();
this.client.helper.waitForElement(selectors.formAddNewEmail).click();
this.enterContactDetails(details);
this.waitForFormTransition();
this.client.helper.waitForElement(selectors.list);
},
tapContact: function(contactName) {
// There are currently no way to search on the text of an HTML Element
// That's why this function do the filter and check that no more than 1
// contact has the name we look up.
var elements = this.client.findElements(Contacts.Selectors.contacts);
var contactsWithName = elements.filter(function(el) {
var data = el.text();
return data && data.indexOf(contactName) > -1;
});
if (contactsWithName.length === 1) {
contactsWithName[0].tap();
} else if (contactsWithName.length > 1) {
throw new Error('There are more than 1 contact that contains "' +
contactName + '"');
} else {
throw new Error('"' + contactName + '" was not found');
}
},
getContactFirstName: function() {
var ci = this.client.findElement(Contacts.Selectors.contactsIframe);
this.client.switchToFrame(ci);
var contactInContactList = this.client.findElement(
Contacts.Selectors.listContactFirst
);
var contactName = contactInContactList.text();
return contactName;
},
tapSave: function() {
this.client.helper.waitForElement(Contacts.Selectors.formSave).click();
},
createNewContactAndReturnToDialer: function(contactName) {
this.client.helper.waitForElement(Contacts.Selectors.formGivenName);
this.client.helper.fillInputField(
Contacts.Selectors.formGivenName, contactName
);
this.client.helper.waitForElement(Contacts.Selectors.formSave).click();
var dialer = this.client.loader.getAppClass('dialer');
dialer.switchTo();
return dialer;
},
tapUpdate: function() {
this.tapSave();
},
get systemMenu() {
var selectors = Contacts.Selectors;
// Switch to the system app first.
this.client.switchToFrame();
return this.client.helper.waitForElement(selectors.systemMenu);
},
/**
* Helper method to simulate clicks on iFrames which is not currently
* working in the Marionette JS Runner.
* @param {Marionette.Element} element The element to simulate the click on.
**/
clickOn: function(element) {
element.scriptWith(function(elementEl) {
var event = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': true
});
elementEl.dispatchEvent(event);
});
},
goToSettings: function() {
this.client.helper.waitForElement(
Contacts.Selectors.settingsButton).tap();
var settings = this.client.helper.waitForElement(
Contacts.Selectors.settingsView);
this.waitForFadeIn(settings);
},
getElementStyle: function(selector, type) {
return this.client.executeScript(function(selector, type) {
return document.querySelector(selector).style[type];
}, [selector, type]);
},
getNumberOfContacts: function() {
return this.contactsNames.length;
},
get contactsNames() {
try {
var contactElements = this.client.findElements(
Contacts.Selectors.contactsItems);
return contactElements.map((element) => {
return element.text();
});
} catch(e) {
return [];
}
}
};
module.exports = Contacts;

View file

@ -0,0 +1,126 @@
'use strict';
/* globals mozContact */
var fs = require('fs');
/* This module contains Contacts Data Layer functions */
function ContactsData(client) {
this.client = client;
}
ContactsData.prototype = {
// Creates a new FB Contact
createFbContact: function() {
this.client.importScript(fs.readFileSync(__dirname +
'/../data/facebook_contact_data.js',
'utf8'));
var saveFBContact = function() {
var LazyLoader = window.wrappedJSObject.LazyLoader;
var data = window.wrappedJSObject.data;
var fb = window.wrappedJSObject.fb;
var Contacts = window.wrappedJSObject.Contacts;
var DEPS = [
'/shared/js/contacts/utilities/image_square.js'
];
Contacts.loadFacebook(function() {
LazyLoader.load(DEPS, function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/contacts/style/images/f_logo.png');
xhr.responseType = 'blob';
xhr.onload = function() {
var fbContact = new fb.Contact();
data.fbContactData.fbInfo.photo = [xhr.response];
fbContact.setData(data.fbContactData);
var savingFBContact = fbContact.save();
savingFBContact.onsuccess = function() {
marionetteScriptFinished(data.fbContactData);
};
savingFBContact.onerror = function() {
marionetteScriptFinished();
};
};
xhr.onerror = marionetteScriptFinished;
xhr.send(null);
});
});
};
var fbContactData;
this.client.executeAsyncScript(saveFBContact, function(err, val) {
if (err) {
console.error(err);
}
fbContactData = val;
});
this.client.waitFor(function() {
return fbContactData;
});
return fbContactData;
},
createMozContact: function(contactData, withPhoto) {
var saveMozContact = function(contactData, withPhoto) {
var doSave = function doSave(contactData) {
var mozContacts = navigator.mozContacts;
var handler = function handler(event) {
mozContacts.removeEventListener('contactchange', handler);
marionetteScriptFinished(event.contactID);
};
mozContacts.addEventListener('contactchange', handler);
var contact = new mozContact(contactData);
var req = mozContacts.save(contact);
req.onerror = function() {
mozContacts.removeEventListener('contactchange', handler);
marionetteScriptFinished();
};
};
if (withPhoto) {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/contacts/style/images/f_logo.png');
xhr.responseType = 'blob';
xhr.onload = function() {
contactData.photo = [xhr.response];
doSave(contactData);
};
xhr.onerror = marionetteScriptFinished;
xhr.send(null);
}
else {
doSave(contactData);
}
};
var contactID;
this.client.executeAsyncScript(saveMozContact, [contactData, withPhoto],
function(err, val) {
if (err) {
console.error(err);
}
contactID = val;
});
this.client.waitFor(function() {
return contactID;
});
return contactID;
}
};
module.exports = ContactsData;

View file

@ -0,0 +1,40 @@
'use strict';
/**
* Abstraction around sms app.
* This file can be moved to the sms app once we
* implement sms marionette tests.
* @constructor
* @param {Marionette.Client} client for operations.
*/
function Sms(client) {
this.client = client;
}
/**
* @type String Origin of sms app
*/
Sms.URL = 'app://sms.gaiamobile.org';
Sms.Selectors = {
listCompose: '#icon-add',
threadAddContact: '#messages-contact-pick-button'
};
Sms.prototype = {
/**
* Launches sms app and focuses on frame.
*/
launch: function() {
this.client.apps.launch(Sms.URL);
this.client.apps.switchToApp(Sms.URL);
this.client.helper.waitForElement('body');
},
relaunch: function() {
this.client.apps.close(Sms.URL);
this.launch();
}
};
module.exports = Sms;

View file

@ -0,0 +1,39 @@
'use strict';
/* global module */
var SELECTORS = Object.freeze({
detailsFavoriteButton: '#toggle-favorite',
detailsHeader: '#details-view-header',
details: '#view-contact-details',
list: '#view-contacts-list'
});
function ContactsDetailsAccessor(client) {
this.client = client;
}
ContactsDetailsAccessor.prototype = {
get favoritesButton() {
return this.client.helper.waitForElement(
SELECTORS.detailsFavoriteButton);
},
get detailsHeader() {
return this.client.helper.waitForElement(
SELECTORS.detailsHeader);
},
get listSelector () {
return SELECTORS.list;
},
get detailsSelector () {
return SELECTORS.details;
},
get detailsHeaderSelector () {
return SELECTORS.detailsHeader;
}
};
module.exports = ContactsDetailsAccessor;

View file

@ -0,0 +1,66 @@
'use strict';
/* global module */
var ContactsDetailsAccessor = require('./accessors');
function ContactsDetailsView(client) {
this.client = client;
this.accessors = new ContactsDetailsAccessor(client);
}
ContactsDetailsView.prototype = {
makeFavorited: function() {
this.accessors.favoritesButton.tap();
this.waitForFavorited;
},
waitForFavorited: function() {
var self = this;
this.client.waitFor(
'waitForHeaderToContainFavoriteClass', function() {
return self.accessors.detailsHeader.getAttribute(
'class').indexOf('favorite') != -1;
});
},
//TO-DO: ADD THIS TO A CONTACT VIEW BASE CLASS
backToList: function() {
this.waitForFadeIn(this.client.helper.waitForElement(
this.accessors.detailsSelector));
var header = this.client.helper.waitForElement(
this.accessors.detailsHeaderSelector);
this.client.loader.getActions().wait(0.5).tap(header, 10, 10).perform();
this.waitSlideLeftList();
},
waitSlideLeftList: function() {
var element = this.client.findElement(
this.accessors.listSelector),
location;
var test = function() {
location = element.location();
return location.x <= 0;
};
this.client.waitFor(test);
},
waitForFadeIn: function(element) {
var test = function() {
var opacity = element.cssProperty('opacity');
var pointerEvents = element.cssProperty('pointer-events');
return opacity == 1 && pointerEvents == 'auto';
};
this.client.waitFor(test);
},
backtoContactsList: function() {
this.backToList();
var ContactsListView = require('../contact-list/view');
var contactsListView = new ContactsListView(this.client);
contactsListView.accessors.firstFavorite;
return contactsListView;
}
};
module.exports = ContactsDetailsView;

View file

@ -0,0 +1,31 @@
'use strict';
/* global module */
var SELECTORS = Object.freeze({
listContactFirstText: 'li:not([data-group="ice"]).contact-item p',
listFavoritesFirstText: '#contacts-list-favorites .contact-item p',
details: '#view-contact-details'
});
function ContactsListAccessor(client) {
this.client = client;
}
ContactsListAccessor.prototype = {
get firstContact() {
return this.client.helper.waitForElement(
SELECTORS.listContactFirstText);
},
get firstFavorite() {
return this.client.helper.waitForElement(
SELECTORS.listFavoritesFirstText);
},
get detailsSelector() {
return SELECTORS.details;
}
};
module.exports = ContactsListAccessor;

View file

@ -0,0 +1,47 @@
'use strict';
/* global module */
var ContactsListAccessor = require('./accessors');
function ContactsListView(client) {
this.client = client;
this.accessors = new ContactsListAccessor(client);
}
ContactsListView.prototype = {
get firstContact() {
return this.accessors.firstContact;
},
get firstFavorite() {
return this.accessors.firstFavorite;
},
waitSlideLeftDetails: function() {
var element = this.client.findElement(
this.accessors.detailsSelector),
location;
var waitUntilPanelReachedLeftBorder = function() {
location = element.location();
return location.x <= 0;
};
this.client.waitFor(waitUntilPanelReachedLeftBorder);
},
goToContact: function() {
this.firstContact.tap();
this.waitSlideLeftDetails();
return this._createContactsDetailsView();
},
_createContactsDetailsView: function() {
var ContactsDetailsView = require('../contact-details/view');
var contactsDetailsView = new ContactsDetailsView(
this.client, this.subject);
return contactsDetailsView;
}
};
module.exports = ContactsListView;

View file

@ -0,0 +1,192 @@
'use strict';
var Contacts = require('./lib/contacts');
var ContactsData = require('./lib/contacts_data');
var assert = require('assert');
marionette('Contacts > List', function() {
var client = marionette.client({
profile: Contacts.config,
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
var subject;
var selectors;
var contactsData;
var actions;
var agenda = [{ givenName: 'a' }, { givenName: 'b' }, { givenName: 'c' },
{ givenName: 'd' }, { givenName: 'e' }, { givenName: 'f' },
{ givenName: 'g' }, { givenName: 'h' }, { givenName: 'i' }];
function getNumberOfItemsVisited() {
return client.executeScript(function() {
return document.querySelectorAll('li[data-visited="true"]').length;
});
}
setup(function() {
contactsData = new ContactsData(client);
actions = client.loader.getActions();
subject = new Contacts(client);
subject.launch();
selectors = Contacts.Selectors;
});
suite('Visited contacts > ', function() {
setup(function() {
agenda.forEach(function(contact) {
subject.addContact(contact);
});
});
test('at least one has been visited by the imageLoader', function() {
assert.ok(getNumberOfItemsVisited() > 0);
});
test('number of visited is the same after leaving search view', function() {
var numItemsVisited = getNumberOfItemsVisited();
client.findElement(selectors.searchLabel).click();
client.helper.waitForElement(selectors.searchCancel).click();
assert.ok(getNumberOfItemsVisited() === numItemsVisited);
});
});
suite('List Order > ', function() {
var data = [['GG', 'E'], ['AA', 'Z'],
['XX', 'C'], ['CC', 'X'], ['EE', 'G'], ['FF', 'F'], ['HH', 'D'],
['BB', 'Y'], ['YY', 'B'], ['ZZ', 'A'], ['DD', 'H']];
setup(function() {
data.forEach(contactData => {
contactsData.createMozContact({
givenName: [contactData[0]],
familyName: [contactData[1]]
}, false);
});
});
function givenNameOrder(a, b) {
return a[0].localeCompare(b[0]);
}
function familyNameOrder(a, b) {
return a[1].localeCompare(b[1]);
}
test('Default to: given name > ', function() {
var names = subject.contactsNames;
var targetOrder = data.sort(givenNameOrder);
names.forEach((name, index) => {
assert.equal(name, targetOrder[index][0] + ' ' + targetOrder[index][1]);
});
});
test('Order by family name > ', function() {
subject.goToSettings();
// Change order
client.switchToShadowRoot(
client.helper.waitForElement(selectors.orderSwitch));
subject.clickOn(client.findElement(selectors.changeOrder));
client.switchToShadowRoot();
subject.clickOn(client.findElement(selectors.settingsClose));
subject.waitForFadeIn(client.helper.waitForElement(selectors.list));
var names = subject.contactsNames;
var targetOrder = data.sort(familyNameOrder);
names.forEach((name, index) => {
assert.equal(name, targetOrder[index][0] + ' ' + targetOrder[index][1]);
});
});
});
suite('Images in list > ', function() {
// Generates 3 contacts for each letter with image
function generateContactsWithImage() {
for(var i = 0; i < 25; i++) {
var name = '';
var character = 97 + i;
for (var j = 0; j < 3; j++) {
name += String.fromCharCode(character);
var contactData = {
givenName: [name]
};
contactsData.createMozContact(contactData, true);
}
}
}
function firstContactHasImage() {
return client.executeScript(function() {
var span = document.querySelector(
'#contacts-list-A .contact-item span');
if (!span) {
return false;
}
return span.dataset.src && span.style.backgroundImage !== null;
});
}
function firstContactHasDefaultImage() {
try {
var span = client.findElement(
'#contacts-list-A .contact-item span');
return span.cssProperty('background-image') === 'url(' +
'"app://communications.gaiamobile.org/contacts/' +
'style/images/Imagery.png")';
} catch (e) {
return false;
}
}
function checkLetterForElement(selector, letter) {
try {
var elem = client.findElement(selector);
return elem.getAttribute('data-group') === letter;
} catch (e) {
return e.toString();
}
}
suite('Default Images > ', function() {
// Moztrap: https://moztrap.mozilla.org/manage/case/14399/
test('contact with no picture show the default one', function() {
subject.addContact({givenName: 'Anthony'});
assert.ok(firstContactHasDefaultImage());
assert.ok(checkLetterForElement('span[data-type="img"]', 'A'));
});
});
suite('Contacts with images > ', function() {
setup(function() {
generateContactsWithImage();
});
test('after scrolling we keep the images', function() {
// Scroll to different sections in the list
var scrollbar = client.helper.waitForElement(
Contacts.Selectors.scrollbar);
//F
actions.press(scrollbar, 15, 100).release().perform();
client.helper.waitForElement(Contacts.Selectors.overlay);
client.helper.waitForElementToDisappear(Contacts.Selectors.overlay);
//T
actions.press(scrollbar, 15, 300).release().perform();
client.helper.waitForElement(Contacts.Selectors.overlay);
client.helper.waitForElementToDisappear(Contacts.Selectors.overlay);
//X
actions.press(scrollbar, 15, 350).release().perform();
client.helper.waitForElement(Contacts.Selectors.overlay);
client.helper.waitForElementToDisappear(Contacts.Selectors.overlay);
//A
actions.press(scrollbar, 15, 40).release().perform();
client.helper.waitForElement(Contacts.Selectors.overlay);
client.helper.waitForElementToDisappear(Contacts.Selectors.overlay);
assert.ok(firstContactHasImage());
});
});
});
});

View file

@ -0,0 +1,7 @@
[form_test.js]
[search_test.js]
[activities_test.js]
[details_test.js]
[contacts_shortcuts_test.js]
[delete_contacts_test.js]
[ice_test.js]

View file

@ -0,0 +1,77 @@
'use strict';
var Contacts = require('./lib/contacts');
var assert = require('assert');
marionette('Contacts > Merge contacts', function() {
var client = marionette.client({
profile: Contacts.config,
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
var subject;
var selectors;
var aTelNumber = '655555555';
var anotherTelNumber = '655555556';
function addTestContact(name, telephone) {
subject.addContact({
givenName: name,
tel: telephone
});
}
function mergeContact(name, telephone) {
subject.mergeContact({
givenName: name,
tel: telephone
});
}
function selectAll(callback) {
// Click on settings button and wait for slide up animation
client.helper.waitForElement(selectors.settingsButton).click();
var settings = client.helper.waitForElement(selectors.settingsView);
subject.waitForSlideUp(settings);
// Click on delete button and wait for slide down animation
client.helper.waitForElement(selectors.bulkDelete).click();
subject.waitForSlideDown(settings);
var editForm = client.findElement(selectors.editForm);
editForm.displayed(callback);
}
function checkHeaderCount(expectedCount) {
client.helper.waitForElement(selectors.selectAllButton).click();
var header = client.helper.waitForElement(selectors.header);
return assert.equal(header.getAttribute('data-l10n-args'),
'{"n":' + expectedCount + '}');
}
setup(function() {
subject = new Contacts(client);
subject.launch();
selectors = Contacts.Selectors;
});
// Disabling these tests by now due to we need a way to switch to a
// different window instead of switching to an app, due to paths can
// differ.
// More info in [1].
// These test must be recovered once this bug will be landed.
// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1140344#c9
test.skip(
'Selection count should be correct after merging contacts',
function() {
addTestContact('Contact1', aTelNumber);
addTestContact('Contact2', anotherTelNumber);
mergeContact('Contact3', anotherTelNumber);
selectAll(function() {
checkHeaderCount(2);
});
}
);
});

View file

@ -0,0 +1,60 @@
'use strict';
/* global MozActivity */
var Contacts = require('./lib/contacts');
var Sms = require('./lib/sms');
var assert = require('assert');
var fs = require('fs');
marionette('Contacts > MultipleSelect', function() {
var client = marionette.client({ profile: Contacts.config });
var subject;
var smsSubject;
var selectors;
setup(function() {
subject = new Contacts(client);
smsSubject = new Sms(client);
selectors = Contacts.Selectors;
});
suite('Import', function() {
function getNumberOfContacts(callback) {
client.executeAsyncScript(function() {
var request = navigator.mozContacts.getCount();
request.onsuccess = function() {
marionetteScriptFinished(request.result);
};
}, callback);
}
setup(function() {
smsSubject.launch();
client.executeScript(function(vCardFile) {
new MozActivity({
name: 'open',
data: {
type: 'text/vcard',
filename: 'vcard_21_multiple.vcf',
blob: new Blob([vCardFile], {type: 'text/vcard'}),
allowSave: true
}
});
}, [fs.readFileSync(__dirname + '/data/vcard_21_multiple.vcf', 'utf8')]);
var iframe = 'iframe[src="' + Contacts.URL +
'/contacts/views/vcard_load/vcard_load.html"]';
client.switchToFrame();
client.switchToFrame(client.findElement(iframe), {'focus': true});
});
test('Importing all contacts', function() {
client.helper.waitForElement(selectors.multipleSelectSave).click();
var status = client.helper.waitForElement(selectors.multipleSelectStatus);
assert.ok(status.text().indexOf('2') >= 0);
getNumberOfContacts(function(err, value) {
assert.equal(value, 2);
});
});
});
});

View file

@ -0,0 +1,76 @@
'use strict';
var Contacts = require('./lib/contacts');
var ContactsData = require('./lib/contacts_data');
var assert = require('assert');
marionette('Contacts > Search', function() {
var client = marionette.client({
profile: Contacts.config,
desiredCapabilities: { raisesAccessibilityExceptions: false }
});
var contactsData = new ContactsData(client);
var subject;
var selectors;
var testContact;
setup(function() {
subject = new Contacts(client);
subject.launch();
testContact = {
tel: [{value: '1231231234', type: ['home']}],
givenName: ['Hello'],
familyName: ['World'],
name: ['Hello World'],
email: [{value: 'hello@example.com', type: ['home']}],
adr: [{type: ['home'], pref: true, streetAddress: 'False Street 123'}],
org: ['Mozilla'],
note: ['This is a note about the contact.']
};
selectors = Contacts.Selectors;
});
suite('Search Mode', function() {
test('Can enter and exit search mode', function() {
contactsData.createMozContact(testContact);
client.helper.waitForElement(selectors.searchLabel).click();
var result = client.helper.waitForElement(selectors.searchResultFirst);
assert.ok(result.displayed(), 'Search view was not opened');
client.helper.waitForElement(selectors.searchCancel).click();
var listView = client.helper.waitForElement(selectors.list);
assert.ok(listView.displayed(), 'List view was not shown');
});
test('Search text is displayed correctly', function() {
var details = details || {
givenName: ['A%%&']
};
contactsData.createMozContact(details);
client.helper.waitForElement(selectors.searchLabel).click();
client.helper.waitForElement(selectors.searchInput).sendKeys('a');
client.helper.waitForElement(selectors.searchResultFirst).text(
function(error, textContent) {
assert.equal('A%%&', textContent);
});
});
test('Search gets updated if contact changes', function() {
contactsData.createMozContact(testContact);
client.helper.waitForElement(selectors.searchLabel).click();
client.helper.waitForElement(selectors.searchResultFirst).click();
client.helper.waitForElement(selectors.detailsEditContact).click();
subject.waitForFormShown();
client.helper.waitForElement(selectors.formGivenName).sendKeys('zzz');
client.findElement(selectors.formSave).click();
client.helper.waitForElement(selectors.detailsContactName).text(
function(error, textContent) {
assert.ok(textContent.indexOf('zzz') > -1);
});
});
});
});

View file

@ -0,0 +1,49 @@
'use strict';
var Contacts = require('./lib/contacts');
var assert = require('assert');
marionette('Contacts > Settings', function() {
var client = marionette.client({ profile: Contacts.config });
var subject;
var selectors;
var buttonSelectors;
setup(function() {
subject = new Contacts(client);
subject.launch();
selectors = Contacts.Selectors;
buttonSelectors = [selectors.exportButton, selectors.bulkDelete,
selectors.setIceButton];
});
function isDisabled(selector) {
return client.executeScript(function(selector) {
return document.querySelector(selector).disabled;
}, [selector]);
}
suite('Export, delete and ice buttons disabled', function() {
test('No contacts -> Disabled', function() {
client.findElement(selectors.settingsButton).click();
var settings = client.helper.waitForElement(selectors.settingsView);
subject.waitForSlideUp(settings);
buttonSelectors.forEach(function(selector) {
assert.ok(isDisabled(selector));
});
});
test('List not empty -> Active', function() {
subject.addContact({
givenName: 'Cristian'
});
client.helper.waitForElement(selectors.settingsButton).click();
var settings = client.helper.waitForElement(selectors.settingsView);
subject.waitForSlideUp(settings);
buttonSelectors.forEach(function(selector) {
assert.ok(!isDisabled(selector));
});
});
});
});

View file

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

View file

@ -0,0 +1,24 @@
/* exported b64Photo */
'use strict';
var b64Photo = 'Qk02BAAAAAAAADYAAAAoAAAAEAAAAPD///8BACAAAAAAAAAAAABiBwAAYgc' +
'AAAAAAAAAAAAA///////////////////////////////////////////////////+//////////' +
'///////////////////////////////////' +
'//////////////////////////V0s3/b21l/1pbVf' +
'9ydHL/u8HB//D09f/29vb///////////////////////////////////////Lx8P9ycWv/EhEO' +
'/wAAAP8AAgD/Gh0Z/15mav+tt7z/6+zr/////////////v////////////////////////+IiY' +
'f/AgYF/wAAAP8AAAD/AAAA/x8iI/88Ojv/e3l6///+/v//////////////////////////////' +
'///p5+f/am5z/xoeI/8iKDH/HSIr/ztATP+wtL//sbfC/zI4P//Ky8r///////////////////' +
'////7+/f/s7O3/vcDH/2Fkbf8+Qk7/foSW/5Wcsv+5vtP/7ezz//////97g5P/j5Oh//f4+P//' +
'////+/v7//Py8v/z8vP/9/j5/8jL2/8zNkD/MDM4/3Nydf/X0+H/0cvZ/7Srrv/Hvrz/h4uX/2' +
'Vsf/+5vcP/5OXj//Tz8//5+fn///////7+/v+eoa3/NjxE/2Vnaf9vbGr/pqSt/+ro8v+xqrP/' +
'6ePi/4SLnP85QE//e4SQ/5CWmv////////////z8+//X1tf/kZWc/09UXP9hY2f/TEhL/62twP' +
'/o6vP/n5ij/9LM0f+WnbX/PkRS/4CFkv99gor//f39//j39//19PP/+Pj3//////+ysrj/cnqP' +
'/21viv/c2On/9PL3//Hv+v/l5/T/x8jd/7Cvv//Y0NX/5NzD//X19f/7+/v///////Py9v/W3u' +
'f/z9Xa/4WFm/9UVGn/tK3D/+/o8v/Y0uD/9fL8/+3q9f/w7fn/+/n+////////////////////' +
'///08/b/2eLo/8XT2P9zdYb/XFxy/8S80//68vz/urTE/9XU5//j4+//9vX6////////////7e' +
'/u/+Dk5v/a4OT/8vf5///////g5+n/kZKe/25of/9sa3D/ZGNn/2pndf/ExdP/0tTf/+nm4//+' +
'/Pr//////87V2v+KmKf/cYOV/3mMn/+NnK3/TVll/1dYZP9kYHn/ODBG/yAcMP9fXW//ubzK/9' +
'PW3/9JREX/OTIu/7OxsP//////y9HZ/4WSov9xfo//TVhx/x8nNP8ZGiH/Y2F//3Bphv9vZYj/' +
'mZm3/7C1yv/Ey9n/DREX/wYIDf9ZaYb//v7+/8vS2v+Hk6T/h4+c/2hxg/8iJi//AwQE/0pHYP' +
'9nX4f/kIuv/4WIo/+GjaT/doOb/wECBv8dIS3/VWWL/w==';

View file

@ -0,0 +1,667 @@
/* global Cache */
'use strict';
requireApp('communications/contacts/js/utilities/performance_helper.js');
requireApp('communications/contacts/js/bootstrap.js');
requireApp('communications/contacts/js/cache.js');
const DUMMY_CONTACT_UUID = '06eecb011f424a97aaa615c26ef66e0c';
const ANOTHER_DUMMY_CONTACT_UUID = 'c7e3e90e2d8d4ade872ba3575deef54d';
const YET_ANOTHER_DUMMY_CONTACT_UUID = '2ac02c47e2984c07a81476e5ae8f40d7';
// Cache of a contact named "Dummy"
const VALID_CACHE_ONE_CONTACT = { content: [
{
'elementName': 'section',
'attributes': [
{
'name': 'id',
'value': 'section-group-D'
},
{
'name': 'class',
'value': 'group-section'
}
],
'innerHTML':
`<header aria-labelledby=\"contacts-listed-abbr-D\" class=\"\"
id=\"group-D\">
<abbr id=\"contacts-listed-abbr-D\" aria-hidden=\"true\"
title=\"Contacts listed D\">D</abbr>
</header>
<ol data-group=\"D\" id=\"contacts-list-D\"
aria-labelledby=\"contacts-listed-abbr-D\" role=\"listbox\">
<li data-cache=\"true\" data-rendered=\"true\"
data-updated=\"1424785158782\" class=\"contact-item\"
data-group=\"D\" role=\"option\"
data-uuid=\"${DUMMY_CONTACT_UUID}\">
<label class=\"contact-checkbox pack-checkbox\">
<input value=\"${DUMMY_CONTACT_UUID}\" name=\"selectIds[]\"
type=\"checkbox\">
<span></span>
</label>
<p class=\"contact-text\">
<bdi><strong>Dummy</strong></bdi>
</p>
</li>
</ol>`
}]
};
// Cache with contacts "Dummy", "AnotherDummy" and "YetAnotherDummy"
const VALID_CACHE_MULTIPLE_CONTACTS = { content: [{
'elementName': 'section',
'attributes': [
{
'name': 'id',
'value': 'section-group-A'
},
{
'name': 'class',
'value': 'group-section'
}
],
'innerHTML':
`<header aria-labelledby=\"contacts-listed-abbr-A\" class=\"\"
id=\"group-A\">
<abbr id=\"contacts-listed-abbr-A\" aria-hidden=\"true\"
title=\"Contacts listed A\">A</abbr>
</header>
<ol data-group=\"A\" id=\"contacts-list-A\"
aria-labelledby=\"contacts-listed-abbr-A\" role=\"listbox\">
<li data-cache=\"true\" data-rendered=\"true\"
data-updated=\"1424803481298\" class=\"contact-item\"
data-group=\"A\" role=\"option\"
data-uuid=\"${ANOTHER_DUMMY_CONTACT_UUID}\">
<label class=\"contact-checkbox pack-checkbox\">
<input value=\"${ANOTHER_DUMMY_CONTACT_UUID}\" name=\"selectIds[]\"
type=\"checkbox\">
<span></span>
</label>
<p class=\"contact-text\">
<bdi><strong>AnotherDummy</strong></bdi>
</p>
</li>
</ol>`
}, {
'elementName': 'section',
'attributes': [
{
'name': 'id',
'value': 'section-group-D'
},
{
'name': 'class',
'value': 'group-section'
}
],
'innerHTML':
`<header aria-labelledby=\"contacts-listed-abbr-D\" class=\"\"
id=\"group-D\">
<abbr id=\"contacts-listed-abbr-D\" aria-hidden=\"true\"
title=\"Contacts listed D\">D</abbr>
</header>
<ol data-group=\"D\" id=\"contacts-list-D\"
aria-labelledby=\"contacts-listed-abbr-D\" role=\"listbox\">
<li data-cache=\"true\" data-rendered=\"true\"
data-updated=\"1424803471199\" class=\"contact-item\"
data-group=\"D\" role=\"option\"
data-uuid=\"${DUMMY_CONTACT_UUID}\">
<label class=\"contact-checkbox pack-checkbox\">
<input value=\"${DUMMY_CONTACT_UUID}\" name=\"selectIds[]\"
type=\"checkbox\">
<span></span>
</label>
<p class=\"contact-text\">
<bdi><strong>Dummy</strong></bdi>
</p>
</li>
</ol>`
}, {
'elementName': 'section',
'attributes': [
{
'name': 'id',
'value': 'section-group-Y'
},
{
'name': 'class',
'value': 'group-section'
}
],
'innerHTML':
`<header aria-labelledby=\"contacts-listed-abbr-Y\" class=\"\"
id=\"group-Y\">
<abbr id=\"contacts-listed-abbr-Y\" aria-hidden=\"true\"
title=\"Contacts listed Y\">Y</abbr>
</header>
<ol data-group=\"Y\" id=\"contacts-list-Y\"
aria-labelledby=\"contacts-listed-abbr-Y\" role=\"listbox\">
<li data-cache=\"true\" data-rendered=\"true\"
data-updated=\"1424803496924\" class=\"contact-item\"
data-group=\"Y\" role=\"option\"
data-uuid=\"${YET_ANOTHER_DUMMY_CONTACT_UUID}\">
<label class=\"contact-checkbox pack-checkbox\">
<input value=\"${YET_ANOTHER_DUMMY_CONTACT_UUID}\"
name=\"selectIds[]\" type=\"checkbox\">
<span></span>
</label>
<p class=\"contact-text\">
<bdi><strong>YetAnotherDummy</strong></bdi>
</p>
</li>
</ol>`
}]
};
const VALID_CACHE_MULTIPLE_CONTACTS_AND_FAVORITE = { content: [{
'elementName': 'section',
'attributes': [
{
'name': 'class',
'value': 'group-section hide'
},
{
'name': 'id',
'value': 'section-group-ice'
},
{
'name': 'data-nonsearchable',
'value': 'true'
}
],
'innerHTML':
`<ol id=\"contact-list-ice\" data-group=\"ice\">
<li data-visited=\"false\" data-rendered=\"false\" data-status=\"\"
data-cache=\"true\" data-group=\"ice\" class=\"contact-item\">
<span></span>
<p data-l10n-id=\"ICEContactsGroup\" class=\"contact-text\">
ICE Contacts
</p>
</li>
</ol>`
}, {
'elementName': 'section',
'attributes': [
{
'name': 'id',
'value': 'section-group-favorites'
},
{
'name': 'class',
'value': 'group-section'
},
{
'name': 'data-nonsearchable',
'value': 'true'
}
],
'innerHTML':
`<header aria-labelledby=\"contacts-listed-abbr-favorites\" class=\"\"
id=\"group-favorites\">
<abbr id=\"contacts-listed-abbr-favorites\" aria-hidden=\"true\"
title=\"Contacts listed favorites\">
</abbr>
</header>
<ol data-group=\"favorites\" id=\"contacts-list-favorites\"
aria-labelledby=\"contacts-listed-abbr-favorites\" role=\"listbox\">
<li data-status=\"\" data-cache=\"true\" data-visited=\"false\"
data-order=\"DUMMY\" data-search=\"Dummy\" data-rendered=\"false\"
data-updated=\"1426068653117\" class=\"contact-item\"
data-group=\"favorites\" role=\"option\"
data-uuid=\"${DUMMY_CONTACT_UUID}\">
<label class=\"contact-checkbox pack-checkbox\">
<input value=\"${DUMMY_CONTACT_UUID}\" name=\"selectIds[]\"
type=\"checkbox\">
<span></span>
</label>
<p class=\"contact-text\"><bdi><strong>Dummy</strong> </bdi></p>
</li>
</ol>`
}, {
'elementName': 'section',
'attributes': [
{
'name': 'id',
'value': 'section-group-A'
},
{
'name': 'class',
'value': 'group-section'
}
],
'innerHTML':
`<header aria-labelledby=\"contacts-listed-abbr-A\" class=\"\"
id=\"group-A\">
<abbr id=\"contacts-listed-abbr-A\" aria-hidden=\"true\"
title=\"Contacts listed A\">A</abbr>
</header>
<ol data-group=\"A\" id=\"contacts-list-A\"
aria-labelledby=\"contacts-listed-abbr-A\" role=\"listbox\">
<li data-status=\"\" data-cache=\"true\" data-visited=\"false\"
data-order=\"ANOTHERDUMMY\" data-search=\"AnotherDummy\"
data-rendered=\"false\" data-updated=\"1426068639081\"
class=\"contact-item\" data-group=\"A\" role=\"option\"
data-uuid=\"${ANOTHER_DUMMY_CONTACT_UUID}\">
<label class=\"contact-checkbox pack-checkbox\">
<input value=\"${ANOTHER_DUMMY_CONTACT_UUID}\" name=\"selectIds[]\"
type=\"checkbox\">
<span></span>
</label>
<p class=\"contact-text\"><bdi><strong>AnotherDummy</strong> </bdi></p>
</li>
</ol>`
}, {
'elementName': 'section',
'attributes': [
{
'name': 'id',
'value': 'section-group-D'
},
{
'name': 'class',
'value': 'group-section'
}
],
'innerHTML':
`<header aria-labelledby=\"contacts-listed-abbr-D\" class=\"\"
id=\"group-D\">
<abbr id=\"contacts-listed-abbr-D\" aria-hidden=\"true\"
title=\"Contacts listed D\">D</abbr>
</header>
<ol data-group=\"D\" id=\"contacts-list-D\"
aria-labelledby=\"contacts-listed-abbr-D\" role=\"listbox\">
<li data-status=\"\" data-cache=\"true\" data-visited=\"false\"
data-order=\"DUMMY\" data-search=\"Dummy\" data-rendered=\"false\"
data-updated=\"1426068653117\" class=\"contact-item\"
data-group=\"D\"
role=\"option\" data-uuid=\"${DUMMY_CONTACT_UUID}\">
<label class=\"contact-checkbox pack-checkbox\">
<input value=\"${DUMMY_CONTACT_UUID}\" name=\"selectIds[]\"
type=\"checkbox\">
<span></span>
</label>
<p class=\"contact-text\"><bdi><strong>Dummy</strong> </bdi></p>
</li>
</ol>`
}
]};
suite('Bootstrap - empty cache', function() {
suiteSetup(function() {
// Make sure that the cache is empty.
localStorage.removeItem('firstChunk');
});
suite('No cache', function() {
setup(function(done) {
Cache.apply('firstChunk').then(done, done);
});
test('Cache apply promise is rejected', function(done) {
Cache.apply('firstChunk').then(() => {
throw new Error('Should not resolve');
}, done);
});
test('Cache should not be active', function() {
assert.isFalse(Cache.active);
});
test('No contact should be cached', function() {
assert.isNull(Cache.contacts);
assert.isNull(Cache.favorites);
assert.equal(Cache.length, 0);
});
test('No header should be cached', function() {
assert.deepEqual(Cache.headers, {});
});
});
});
suite('Bootstrap - Valid cache, one contact', function() {
var fakeContainer;
suiteSetup(function() {
localStorage.setItem('firstChunk',
JSON.stringify(VALID_CACHE_ONE_CONTACT));
fakeContainer = document.createElement('div');
fakeContainer.id = 'groups-list';
document.body.appendChild(fakeContainer);
});
suiteTeardown(function() {
Cache.cleanup();
localStorage.removeItem('firstChunk');
document.body.removeChild(fakeContainer);
});
suite('Cache with one contact', function() {
setup(function(done) {
Cache.apply('firstChunk').then(done);
});
test('Cache should be active', function() {
assert.isTrue(Cache.active);
});
test('Contacts should be cached', function() {
assert.isDefined(Cache.contacts);
assert.equal(Cache.length, 1);
assert.isTrue(Cache.hasContact(DUMMY_CONTACT_UUID));
assert.isFalse(Cache.hasFavorite(DUMMY_CONTACT_UUID));
});
test('Header "D" should be cached for contact "Dummy"', function() {
assert.isDefined(Cache.headers.D);
});
test('Cache content should not be kept in memory', function() {
assert.isNull(Cache._rawContent);
});
test('Getting the contact should remove it from memory', function() {
assert.equal(Cache.length, 1);
Cache.getContact(DUMMY_CONTACT_UUID);
assert.equal(Cache.length, 0);
assert.isUndefined(Cache.getContact(DUMMY_CONTACT_UUID));
});
test('Cache content should be applied to the DOM', function() {
var contactsListD =
fakeContainer.querySelector('ol#contacts-list-D');
assert.isDefined(contactsListD);
assert.equal(contactsListD.children.length, 1);
});
test('Evicting cache should remove cache from disk and memory',
function() {
assert.isDefined(localStorage.getItem('firstChunk'));
Cache.oneviction = () => {
assert.isNull(localStorage.getItem('firstChunk'));
assert.isNull(Cache.contacts);
assert.isNull(Cache.favorites);
assert.deepEqual(Cache.headers, {});
};
Cache.evict();
});
});
});
suite('Bootstrap - Valid cache, multiple contacts', function() {
var fakeContainer;
suiteSetup(function() {
localStorage.setItem('firstChunk',
JSON.stringify(VALID_CACHE_MULTIPLE_CONTACTS));
fakeContainer = document.createElement('div');
fakeContainer.id = 'groups-list';
document.body.appendChild(fakeContainer);
});
suiteTeardown(function() {
Cache.cleanup();
localStorage.removeItem('firstChunk');
document.body.removeChild(fakeContainer);
});
suite('Cache with multiple contacts', function() {
setup(function(done) {
Cache.apply('firstChunk').then(done);
});
test('Cache should be active', function() {
assert.isTrue(Cache.active);
});
test('Contacts should be cached', function() {
assert.isDefined(Cache.contacts);
assert.equal(Cache.length, 3);
assert.isTrue(Cache.hasContact(DUMMY_CONTACT_UUID));
assert.isTrue(Cache.hasContact(ANOTHER_DUMMY_CONTACT_UUID));
assert.isTrue(Cache.hasContact(YET_ANOTHER_DUMMY_CONTACT_UUID));
assert.isFalse(Cache.hasFavorite(DUMMY_CONTACT_UUID));
assert.isFalse(Cache.hasFavorite(ANOTHER_DUMMY_CONTACT_UUID));
assert.isFalse(Cache.hasFavorite(YET_ANOTHER_DUMMY_CONTACT_UUID));
});
test('Headers should be cached for contacts', function() {
assert.isDefined(Cache.headers.D);
assert.isDefined(Cache.headers.A);
assert.isDefined(Cache.headers.Y);
});
test('Cache content should not be kept in memory', function() {
assert.isNull(Cache._rawContent);
});
test('Getting the contact should remove it from memory', function() {
assert.equal(Cache.length, 3);
Cache.getContact(DUMMY_CONTACT_UUID);
assert.equal(Cache.length, 2);
Cache.getContact(ANOTHER_DUMMY_CONTACT_UUID);
assert.equal(Cache.length, 1);
Cache.getContact(YET_ANOTHER_DUMMY_CONTACT_UUID);
assert.equal(Cache.length, 0);
assert.isUndefined(Cache.getContact(DUMMY_CONTACT_UUID));
assert.isUndefined(Cache.getContact(ANOTHER_DUMMY_CONTACT_UUID));
assert.isUndefined(Cache.getContact(YET_ANOTHER_DUMMY_CONTACT_UUID));
});
test('Cache content should be applied to the DOM', function() {
var contactDSection = fakeContainer.querySelector('.section-group-D');
var contactASection = fakeContainer.querySelector('.section-group-A');
var contactYSection = fakeContainer.querySelector('.section-group-Y');
assert.isDefined(contactDSection);
assert.isDefined(contactASection);
assert.isDefined(contactYSection);
});
test('Evicting cache should remove cache from disk and memory',
function() {
assert.isDefined(localStorage.getItem('firstChunk'));
Cache.oneviction = () => {
assert.isNull(localStorage.getItem('firstChunk'));
assert.isNull(Cache.contacts);
assert.isNull(Cache.favorites);
assert.deepEqual(Cache.headers, {});
};
Cache.evict();
});
});
});
suite('Bootstrap - Valid cache, multiple contacts, favorites', function() {
var fakeContainer;
suiteSetup(function() {
localStorage.setItem('firstChunk',
JSON.stringify(VALID_CACHE_MULTIPLE_CONTACTS_AND_FAVORITE));
fakeContainer = document.createElement('div');
fakeContainer.id = 'groups-list';
document.body.appendChild(fakeContainer);
});
suiteTeardown(function() {
Cache.cleanup();
localStorage.removeItem('firstChunk');
document.body.removeChild(fakeContainer);
});
suite('Cache with multiple contacts and favorites', function() {
setup(function(done) {
Cache.apply('firstChunk').then(done);
});
test('Cache should be active', function() {
assert.isTrue(Cache.active);
});
test('Contacts should be cached', function() {
assert.isDefined(Cache.contacts);
assert.equal(Cache.length, 3);
assert.isTrue(Cache.hasContact(DUMMY_CONTACT_UUID));
assert.isTrue(Cache.hasContact(ANOTHER_DUMMY_CONTACT_UUID));
assert.isTrue(Cache.hasFavorite(DUMMY_CONTACT_UUID));
assert.isFalse(Cache.hasFavorite(ANOTHER_DUMMY_CONTACT_UUID));
});
test('Headers should be cached for contacts', function() {
assert.isDefined(Cache.headers.D);
assert.isDefined(Cache.headers.A);
assert.isDefined(Cache.headers.favorites);
});
test('Cache content should not be kept in memory', function() {
assert.isNull(Cache._rawContent);
});
test('Getting the contact should remove it from memory', function() {
assert.equal(Cache.length, 3);
Cache.getContact(DUMMY_CONTACT_UUID);
assert.equal(Cache.length, 2);
Cache.getContact(ANOTHER_DUMMY_CONTACT_UUID);
assert.equal(Cache.length, 1);
Cache.getFavorite(DUMMY_CONTACT_UUID);
assert.equal(Cache.length, 0);
assert.isUndefined(Cache.getContact(DUMMY_CONTACT_UUID));
assert.isUndefined(Cache.getContact(ANOTHER_DUMMY_CONTACT_UUID));
assert.isUndefined(Cache.getFavorite(DUMMY_CONTACT_UUID));
});
test('Cache content should be applied to the DOM', function() {
var contactDSection = fakeContainer.querySelector('.section-group-D');
var contactASection = fakeContainer.querySelector('.section-group-A');
var favoritesSection =
fakeContainer.querySelector('.section-group-favorites');
assert.isDefined(contactDSection);
assert.isDefined(contactASection);
assert.isDefined(favoritesSection);
});
test('Evicting cache should remove cache from disk and memory',
function() {
assert.isDefined(localStorage.getItem('firstChunk'));
Cache.oneviction = () => {
assert.isNull(localStorage.getItem('firstChunk'));
assert.isNull(Cache.contacts);
assert.isNull(Cache.favorites);
assert.deepEqual(Cache.headers, {});
};
Cache.evict();
});
});
});
suite('Bootstrap - Evict and undo cache', function() {
var fakeContainer;
suiteSetup(function() {
fakeContainer = document.createElement('div');
fakeContainer.id = 'groups-list';
document.body.appendChild(fakeContainer);
});
suiteTeardown(function() {
Cache.cleanup();
localStorage.removeItem('firstChunk');
document.body.removeChild(fakeContainer);
});
suite('Evict and undo cache', function() {
var alreadyDone;
var spy;
setup(function(done) {
localStorage.setItem('firstChunk',
JSON.stringify(VALID_CACHE_ONE_CONTACT));
Cache.apply('firstChunk').then(done);
spy = sinon.spy(window, 'setTimeout');
});
teardown(function() {
spy.restore();
});
test('Cache content should be applied to the DOM', function() {
var contactsListD =
fakeContainer.querySelector('ol#contacts-list-D');
assert.equal(contactsListD.children.length, 1);
});
test('Evicting cache should remove cache from disk and memory ' +
'and from the DOM', function(done) {
assert.isDefined(localStorage.getItem('firstChunk'));
Cache.oneviction = () => {
// We will be evicting the cache again in following tests
// and we don't want to run this again.
if (alreadyDone) {
return;
}
assert.isNull(localStorage.getItem('firstChunk'));
assert.isNull(Cache.contacts);
assert.isNull(Cache.favorites);
assert.deepEqual(Cache.headers, {});
var contactsListD =
fakeContainer.querySelector('ol#contacts-list-D');
assert.equal(contactsListD.children.length, 0);
assert.isTrue(spy.called);
alreadyDone = true;
done();
};
Cache.evict(true /* undo */, false /* inmediate */);
});
});
});
suite('Bootstrap - Evict cache right away', function() {
var fakeContainer;
suiteSetup(function() {
fakeContainer = document.createElement('div');
fakeContainer.id = 'groups-list';
document.body.appendChild(fakeContainer);
});
suiteTeardown(function() {
Cache.cleanup();
localStorage.removeItem('firstChunk');
document.body.removeChild(fakeContainer);
});
suite('Evict now!', function() {
var spy;
setup(function(done) {
localStorage.setItem('firstChunk',
JSON.stringify(VALID_CACHE_ONE_CONTACT));
Cache.apply('firstChunk').then(done);
spy = sinon.spy(window, 'setTimeout');
});
teardown(function() {
spy.restore();
});
test('Cache content should be applied to the DOM', function() {
var contactsListD =
fakeContainer.querySelector('ol#contacts-list-D');
assert.equal(contactsListD.children.length, 1);
});
test('Evicting cache should remove cache from disk and memory ' +
'and from the DOM', function(done) {
assert.isDefined(localStorage.getItem('firstChunk'));
Cache.oneviction = () => {
assert.isNull(localStorage.getItem('firstChunk'));
assert.isNull(Cache.contacts);
assert.isNull(Cache.favorites);
assert.deepEqual(Cache.headers, {});
assert.isFalse(spy.called);
done();
};
Cache.evict(false /* undo */, true /* inmediate */);
});
});
});

View file

@ -0,0 +1,169 @@
'use strict';
/* global Cache */
/* global Contacts */
/* global ICEData */
/* global MockContactsListObj */
/* global MocksHelper */
/* global MockMozContacts */
/* global MockMozL10n */
/* global MockNavigationStack */
/* global MockUtils */
/* global MockLoader */
requireApp('communications/contacts/test/unit/mock_header_ui.js');
requireApp('communications/contacts/test/unit/mock_activities.js');
requireApp('communications/contacts/test/unit/mock_l10n.js');
requireApp('communications/contacts/test/unit/mock_cache.js');
requireApp('communications/contacts/test/unit/mock_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_event_listeners.js');
requireApp('communications/contacts/test/unit/mock_asyncstorage.js');
requireApp('communications/contacts/test/unit/mock_loader.js');
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/js/utilities/ice_data.js');
require('/shared/test/unit/mocks/mock_ice_store.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
var mocksForCacheEviction = new MocksHelper([
'ActivityHandler',
'asyncStorage',
'Cache',
'Contacts',
'ICEStore',
'LazyLoader',
'HeaderUI'
]).init();
if (!window.navigationStack) {
window.navigationStack = null;
}
if (!window.contacts) {
window.contacts = null;
}
if (!window.utils) {
window.utils = null;
}
if (!navigator.mozContacts) {
navigator.mozContacts = null;
}
suite('Contacts', function() {
var realNavigationStack;
var realMozL10n;
var realContacts;
var realUtils;
var mockNavigation;
var realMozContacts;
var realLoader;
mocksForCacheEviction.attachTestHelpers();
suiteSetup(function(done) {
mocksForCacheEviction.suiteSetup();
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockMozL10n;
realMozContacts = navigator.mozContacts;
navigator.mozContacts = MockMozContacts;
realContacts = window.contacts;
window.contacts = {};
window.contacts.List = MockContactsListObj;
realUtils = window.utils;
window.utils = MockUtils;
realNavigationStack = window.navigationStack;
window.navigationStack = MockNavigationStack;
realLoader = window.Loader;
window.Loader = MockLoader;
sinon.spy(window, 'navigationStack');
requireApp('communications/contacts/js/utilities/performance_helper.js');
requireApp('communications/contacts/js/main_navigation.js');
requireApp('communications/contacts/js/contacts.js', done);
});
suiteTeardown(function() {
navigator.mozL10n = realMozL10n;
navigator.mozContacts = realMozContacts;
window.contacts = realContacts;
window.utils = realUtils;
window.loader = realLoader;
window.navigationStack.restore();
window.navigationStack = realNavigationStack;
});
setup(function() {
navigator.addIdleObserver = function() {};
loadBodyHTML('/contacts/index.html');
Contacts.init();
mockNavigation = window.navigationStack.firstCall.thisValue;
navigator.mozL10n.fireOnce();
});
suite('oncontactchange', function() {
var evictSpy;
setup(function() {
evictSpy = sinon.spy(Cache, 'evict');
});
teardown(function() {
evictSpy.restore();
});
test('Should evict cache', function() {
var evt = new CustomEvent('contactchange');
navigator.mozContacts.dispatchEvent(evt);
sinon.assert.calledOnce(Cache.evict);
});
});
suite('ICE', function() {
var evictSpy;
setup(function() {
evictSpy = sinon.spy(Cache, 'evict');
});
teardown(function() {
evictSpy.restore();
});
test('Setting a contact as ICE should evict cache', function() {
ICEData.setICEContact(2, 1, true).then(function() {
sinon.assert.calledOnce(Cache.evict);
});
});
});
suite('Locale change', function() {
var maybeEvictSpy;
setup(function() {
maybeEvictSpy = sinon.spy(Cache, 'maybeEvict');
window.contacts = realContacts;
navigator.mozL10n.fireReady();
});
teardown(function() {
maybeEvictSpy.restore();
});
test('Locale change should evict cache', function() {
sinon.assert.calledOnce(Cache.maybeEvict);
});
});
});

View file

@ -0,0 +1,60 @@
/* globals ActionMenu */
'use strict';
requireApp('communications/contacts/test/unit/mock_l10n.js');
suite('Test ActionMenu', function() {
var fakeDOM;
var fakeform;
var fakeCallback;
var real_;
suiteSetup(function(done) {
real_ = window._;
window._ = navigator.mozL10n.get;
fakeDOM = document.createElement('div');
fakeDOM.innerHTML =
'<form id="action-menu" role="dialog" data-type="action" class="hide">' +
'<header id="org-title" data-l10n-id="select_recipient">' +
'Select recipient' +
'</header>' +
'<menu id="value-menu" type="toolbar">' +
'</menu>' +
'</form>';
document.body.appendChild(fakeDOM);
fakeform = document.getElementById('action-menu');
fakeCallback = function() {};
requireApp('communications/contacts/js/action_menu.js', done);
});
suiteTeardown(function() {
window._ = real_;
fakeDOM.parentNode.removeChild(fakeDOM);
});
suite('Invoke function', function() {
var actionmenu;
setup(function() {
actionmenu = new ActionMenu();
});
test('invoke show()', function() {
actionmenu.addToList('123456789', fakeCallback);
actionmenu.addToList('111111111', fakeCallback);
actionmenu.addToList('222222222', fakeCallback);
actionmenu.show();
var buttons = fakeDOM.querySelectorAll('button');
assert.isFalse(fakeform.classList.contains('hide'));
assert.equal(buttons.length, 4); // addToList + canncel
});
test('invoke hide()', function() {
actionmenu.hide();
assert.isTrue(fakeform.classList.contains('hide'));
assert.equal(fakeDOM.querySelectorAll('button').length, 0);
});
});
});

View file

@ -0,0 +1,415 @@
/* globals ActivityHandler, VcardActivityHandler, ConfirmDialog,
MockContactAllFields, MocksHelper, MockMozL10n
*/
'use strict';
require('/shared/test/unit/mocks/mock_contact_all_fields.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/js/contacts/import/utilities/misc.js');
require('/shared/js/contact2vcard.js');
require('/shared/js/text_normalizer.js');
require('/shared/js/setImmediate.js');
requireApp('communications/contacts/test/unit/mock_header_ui.js');
requireApp('communications/contacts/js/activities.js');
requireApp('communications/contacts/js/activities_vcard.js');
requireApp('communications/contacts/test/unit/mock_l10n.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_action_menu.js');
require('/shared/test/unit/mocks/mock_confirm_dialog.js');
require('/shared/test/unit/mocks/mock_moz_contact.js');
if (!window._) {
window._ = null;
}
if (!window.utils) {
window.utils = null;
}
var mocksHelperForActivities = new MocksHelper([
'MainNavigation',
'Contacts',
'ConfirmDialog',
'LazyLoader',
'mozContact',
'HeaderUI'
]).init();
suite('Test Activities', function() {
var realMozL10n,
real_,
realImport;
mocksHelperForActivities.attachTestHelpers();
suiteSetup(function() {
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockMozL10n;
real_ = window._;
window._ = navigator.mozL10n.get;
if (!window.utils) {
window.utils = {};
}
window.utils.overlay = {
show: function() {},
hide: function() {}
};
window.utils.importedCount = null;
window.utils.importedID = null;
realImport = window.utils.importFromVcard;
window.utils.importFromVcard = function(file, callback) {
callback(this.importedCount, this.importedID);
};
});
suiteTeardown(function() {
navigator.mozL10n = realMozL10n;
window._ = real_;
window.utils.importFromVcard = realImport;
});
function assertIsOpened(activity, viewName) {
ActivityHandler.handle(activity);
assert.include(document.location.hash, viewName);
assert.equal(ActivityHandler._currentActivity, activity);
}
suite('Activity launching', function() {
setup(function() {
ActivityHandler._currentActivity = null;
ActivityHandler._launchedAsInlineActivity = false;
window.utils.importedID = null;
document.location.hash = '';
this.sinon.spy(ActivityHandler, 'isCancelable');
this.sinon.spy(VcardActivityHandler, 'handle');
});
test('New contact', function() {
var activity = {
source: {
name: 'new',
data: {}
}
};
assertIsOpened(activity, 'view-contact-form');
});
test('Open contact calls for vcard handling', function() {
var activity = {
source: {
name: 'open',
data: {}
}
};
ActivityHandler.handle(activity);
assert.isTrue(VcardActivityHandler.handle.called);
});
test('Import contact calls for vcard handling', function() {
var activity = {
source: {
name: 'import',
data: {
blob: 'blob'
}
}
};
ActivityHandler.handle(activity);
assert.isTrue(VcardActivityHandler.handle.called);
});
test('Update contact', function() {
var activity = {
source: {
name: 'update',
data: {}
}
};
assertIsOpened(activity, 'add-parameters');
assert.isTrue(ActivityHandler.isCancelable.called,
'checks for activity UI specifics');
});
test('Pick contact', function() {
ActivityHandler._launchedAsInlineActivity = true;
var activity = {
source: {
name: 'pick',
data: {}
}
};
ActivityHandler.handle(activity);
assert.isTrue(ActivityHandler.isCancelable.called,
'checks for activity UI specifics');
assert.equal(ActivityHandler._currentActivity, activity);
});
});
suite('Pick activity handling', function() {
var activity,
contact,
result;
setup(function() {
activity = {
source: {
name: 'pick',
data: {
type: 'webcontacts/tel'
}
},
postResult: function(response) {
result = response;
}
};
contact = new MockContactAllFields();
});
teardown(function() {
activity = {};
contact = {};
result = {};
ConfirmDialog.hide();
});
test('text/vcard, 1 result', function(done) {
var vcardActivity = {
source: {
name: 'pick',
data: {
}
}
};
vcardActivity.source.data.type = 'text/vcard';
ActivityHandler._currentActivity = vcardActivity;
var stub = sinon.stub(window, 'ContactToVcardBlob',
function(aContact, cb) {
cb(new Blob(['1234567'], {
type: 'text/vcard'
}));
});
vcardActivity.postResult = function(response) {
assert.equal(response.name, contact.givenName[0] + '_' +
contact.familyName[0] + '.vcf');
assert.equal(response.blob.type, 'text/vcard');
stub.restore();
done();
};
ActivityHandler.dataPickHandler(contact);
});
test('webcontacts/tel, 0 results', function() {
activity.source.data.type = 'webcontacts/tel';
ActivityHandler._currentActivity = activity;
contact.tel = [];
ActivityHandler.dataPickHandler(contact);
assert.isTrue(ConfirmDialog.showing);
assert.isNull(ConfirmDialog.title);
assert.equal(ConfirmDialog.text, window._('no_contact_phones'));
});
test('webcontacts/tel, 1 result', function() {
activity.source.data.type = 'webcontacts/tel';
ActivityHandler._currentActivity = activity;
// We want to test only with one phone, so erase the last one
contact.tel.pop();
// we need to create a object from data to compare prototypes
// check activities.js > function copyContactData
var newContact = contact;
ActivityHandler.dataPickHandler(newContact);
assert.isFalse(ConfirmDialog.showing);
// Check if all the properties are the same
contact = window.utils.misc.toMozContact(contact);
for (var prop in contact) {
if (prop === 'photo' && result[prop] && result[prop][0]) {
assert.equal(result[prop][0].size, contact[prop][0].size);
assert.equal(result[prop][0].type, contact[prop][0].type);
} else {
assert.equal(JSON.stringify(result[prop]),
JSON.stringify(contact[prop]));
}
}
});
test('webcontacts/tel, many results', function() {
activity.source.data.type = 'webcontacts/tel';
ActivityHandler._currentActivity = activity;
// we need to create a object from data to compare prototypes
// check activities.js > function copyContactData
var newContact = Object.create(contact);
sinon.stub(window.utils.misc, 'toMozContact',
function() {return newContact;});
ActivityHandler.dataPickHandler(newContact);
assert.isFalse(ConfirmDialog.showing);
// Mock returns always the first option from the select, so we need
// to compare to a contact with only the first phone
// As is filtered, we only retrieve one phone number
assert.equal(result.tel.length, 1);
// As the mock of action menu is giving us the first option
// we ensure that this option is the one filtered as well.
assert.equal(newContact.tel[0].value, result.tel[0].value);
// Restore the function
window.utils.misc.toMozContact.restore();
});
test('webcontacts/contact, 0 results', function() {
activity.source.data.type = 'webcontacts/contact';
ActivityHandler._currentActivity = activity;
contact.tel = [];
ActivityHandler.dataPickHandler(contact);
assert.isTrue(ConfirmDialog.showing);
assert.isNull(ConfirmDialog.title);
assert.equal(ConfirmDialog.text, window._('no_contact_phones'));
});
test('webcontacts/contact, 1 result', function() {
activity.source.data.type = 'webcontacts/contact';
ActivityHandler._currentActivity = activity;
contact.tel.pop();
ActivityHandler.dataPickHandler(contact);
assert.isFalse(ConfirmDialog.showing);
assert.equal(result.number, contact.tel[0].value);
});
test('webcontacts/contact, many results', function() {
activity.source.data.type = 'webcontacts/contact';
ActivityHandler._currentActivity = activity;
ActivityHandler.dataPickHandler(contact);
assert.isFalse(ConfirmDialog.showing);
// Mock returns always the first option from the select
assert.equal(result.number, contact.tel[0].value);
});
test('webcontacts/contact, returning a Contact', function() {
activity.source.data.type = 'webcontacts/contact';
activity.source.data.fullContact = true;
ActivityHandler._currentActivity = activity;
contact.tel.pop();
ActivityHandler.dataPickHandler(contact);
assert.isFalse(ConfirmDialog.showing);
assert.deepEqual(result.tel, contact.tel);
assert.deepEqual(result.email, contact.email);
assert.equal(result.id, contact.id);
});
test('webcontacts/email, 0 results', function() {
activity.source.data.type = 'webcontacts/email';
ActivityHandler._currentActivity = activity;
contact.email = [];
ActivityHandler.dataPickHandler(contact);
assert.isTrue(ConfirmDialog.showing);
assert.isNull(ConfirmDialog.title);
assert.equal(ConfirmDialog.text, window._('no_contact_email'));
});
test('webcontacts/email, 1 result', function() {
activity.source.data.type = 'webcontacts/email';
ActivityHandler._currentActivity = activity;
ActivityHandler.dataPickHandler(contact);
assert.isFalse(ConfirmDialog.showing);
assert.equal(result.email, contact.email[0].value);
});
test('webcontacts/email, many results', function() {
activity.source.data.type = 'webcontacts/email';
ActivityHandler._currentActivity = activity;
ActivityHandler.dataPickHandler(contact);
assert.isFalse(ConfirmDialog.showing);
// Mock returns always the first option from the select
assert.equal(result.email, contact.email[0].value);
});
test('webcontacts/select, 0 results', function() {
activity.source.data.type = 'webcontacts/select';
activity.source.data.contactProperties = ['tel', 'email'];
ActivityHandler._currentActivity = activity;
contact.tel = [];
contact.email = [];
ActivityHandler.dataPickHandler(contact);
assert.isTrue(ConfirmDialog.showing);
assert.isNull(ConfirmDialog.title);
assert.equal(ConfirmDialog.text, window._('no_contact_data_available'));
});
test('webcontacts/select, 1 results(tel)', function() {
activity.source.data.type = 'webcontacts/select';
activity.source.data.contactProperties = ['tel', 'email'];
ActivityHandler._currentActivity = activity;
contact.tel.pop();
contact.email = [];
var newContact = Object.create(contact);
sinon.stub(window.utils.misc, 'toMozContact',
function() {return newContact;});
ActivityHandler.dataPickHandler(newContact);
contact = window.utils.misc.toMozContact(contact);
assert.isFalse(ConfirmDialog.showing);
assert.equal(JSON.stringify(result.select),
JSON.stringify(contact.tel));
assert.equal(JSON.stringify(result.contact),
JSON.stringify(contact));
window.utils.misc.toMozContact.restore();
});
test('webcontacts/select, 1 results(email)', function() {
activity.source.data.type = 'webcontacts/select';
activity.source.data.contactProperties = ['tel', 'email'];
ActivityHandler._currentActivity = activity;
contact.tel = [];
contact.email.pop();
var newContact = Object.create(contact);
sinon.stub(window.utils.misc, 'toMozContact',
function() {return newContact;});
ActivityHandler.dataPickHandler(newContact);
contact = window.utils.misc.toMozContact(contact);
assert.isFalse(ConfirmDialog.showing);
assert.equal(JSON.stringify(result.select),
JSON.stringify(contact.email));
assert.equal(JSON.stringify(result.contact),
JSON.stringify(contact));
window.utils.misc.toMozContact.restore();
});
test('webcontacts/select, many results(tel)', function() {
activity.source.data.type = 'webcontacts/select';
activity.source.data.contactProperties = ['tel', 'email'];
ActivityHandler._currentActivity = activity;
contact.email = [];
var newContact = Object.create(contact);
sinon.stub(window.utils.misc, 'toMozContact',
function() {return newContact;});
ActivityHandler.dataPickHandler(newContact);
assert.isFalse(ConfirmDialog.showing);
assert.equal(newContact.tel[0].value, result.select[0].value);
window.utils.misc.toMozContact.restore();
});
test('webcontacts/select, many results(email)', function() {
activity.source.data.type = 'webcontacts/select';
activity.source.data.contactProperties = ['tel', 'email'];
ActivityHandler._currentActivity = activity;
contact.tel = [];
var newContact = Object.create(contact);
sinon.stub(window.utils.misc, 'toMozContact',
function() {return newContact;});
ActivityHandler.dataPickHandler(newContact);
assert.isFalse(ConfirmDialog.showing);
assert.equal(newContact.email[0].value, result.select[0].value);
});
});
});

View file

@ -0,0 +1,96 @@
/*jshint node: true, browser: true */
/* globals MocksHelper, MockLoader, BulkDelete, ConfirmDialog */
'use strict';
requireApp('communications/contacts/test/unit/mock_l10n.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
requireApp('communications/contacts/test/unit/mock_confirm_dialog.js');
requireApp('communications/contacts/test/unit/mock_loader.js');
requireApp('communications/contacts/js/contacts_bulk_delete.js');
var mocksHelperForContactsBulkDelete = new MocksHelper([
'ConfirmDialog'
]).init();
/* jshint ignore:start */
if (!this.utils) {
this.utils = null;
}
/* jshint ignore:end */
suite('contacts_bulk_delete.js', function() {
var realOverlay = null;
var realLoader = null;
var promise = {};
var confirmShowSpy, confirmHideSpy, spies;
mocksHelperForContactsBulkDelete.attachTestHelpers();
suiteSetup(function() {
if (!window.utils) {
window.utils = {};
}
realOverlay = window.utils.overlay;
window.utils.overlay = {
show: function() {}
};
realLoader = window.Loader;
window.Loader = MockLoader;
});
suiteTeardown(function() {
window.utils.overlay = realOverlay;
window.Loader = realLoader;
});
setup(function() {
confirmShowSpy = sinon.spy(ConfirmDialog, 'show');
confirmHideSpy = sinon.spy(ConfirmDialog, 'hide');
spies = [confirmShowSpy, confirmHideSpy];
});
teardown(function() {
spies.forEach(function(spy) {
spy.restore();
});
});
function assertPerformDeleteSuccess(numberOfContacts) {
BulkDelete.performDelete(promise);
promise.onsuccess({
length: numberOfContacts
});
assert.isTrue(confirmShowSpy.called);
assert.isNull(ConfirmDialog.title);
assert.deepEqual(ConfirmDialog.text,
{ id: 'ContactConfirmDel', args: { n: 69 } });
assert.equal(ConfirmDialog.noObject.title, 'cancel');
assert.equal(ConfirmDialog.yesObject.title, 'delete');
}
test('call performDelete successfully and user confirms', function(done) {
var doDeleteStub = sinon.stub(BulkDelete, 'doDelete', function() {
doDeleteStub.restore();
done();
});
assertPerformDeleteSuccess(69);
// Confirmed by user
ConfirmDialog.executeYes();
assert.isTrue(confirmHideSpy.called);
});
test('call performDelete successfully and user cancels', function() {
assertPerformDeleteSuccess(69);
// Cancelled by user
ConfirmDialog.executeNo();
assert.isTrue(confirmHideSpy.called);
});
});

View file

@ -0,0 +1,328 @@
/* globals MocksHelper, ContactsButtons, MockL10n, MockContactAllFields,
MockContactsButtonsDom, LazyLoader, TelephonyHelper,
MultiSimActionButton, ActivityHandler, MockActivityHandler,
SmsIntegration */
'use strict';
require('/shared/js/contacts/contacts_buttons.js');
require('/shared/js/text_normalizer.js');
require('/shared/js/contacts/utilities/dom.js');
require('/shared/js/contacts/utilities/templates.js');
require('/shared/test/unit/mocks/mock_multi_sim_action_button.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/shared/test/unit/mocks/mock_contact_all_fields.js');
require('/shared/test/unit/mocks/dialer/mock_telephony_helper.js');
require('/contacts/test/unit/mock_navigation.js');
require('/contacts/test/unit/mock_contacts.js');
require('/contacts/test/unit/mock_contacts_buttons_dom.js.html');
require('/contacts/test/unit/mock_utils.js');
require('/contacts/test/unit/mock_activities.js');
require('/contacts/test/unit/mock_sms_integration.js');
var mocksHelperForContactsButtons = new MocksHelper([
'LazyLoader',
'ActivityHandler',
'Contacts',
'MultiSimActionButton',
'TelephonyHelper',
'SmsIntegration'
]).init();
suite('Render contact', function() {
var subject;
var realL10n;
var mockContact;
var container;
var listDetails;
var getSpy;
var assertCarrierWrapperVisibility = function(visibility) {
var carrierWrapperElt = listDetails.querySelector('.carrier-wrapper');
assert.equal(carrierWrapperElt.hidden, !visibility);
};
mocksHelperForContactsButtons.attachTestHelpers();
suiteSetup(function() {
subject = ContactsButtons;
realL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;
});
setup(function() {
getSpy = this.sinon.spy(MockL10n, 'get');
mockContact = new MockContactAllFields(true);
document.body.innerHTML = MockContactsButtonsDom;
container = document.getElementById('contact-detail');
listDetails = document.getElementById('list-details');
subject.init(listDetails, container, MockActivityHandler);
});
teardown(function() {
document.body.innerHTML = '';
});
suite('Render phones', function() {
test('with 1 phone', function() {
subject.renderPhones(mockContact);
assert.include(container.innerHTML, 'phone-details-template-0');
assert.include(container.innerHTML, mockContact.tel[0].value);
assert.include(container.innerHTML, mockContact.tel[0].carrier);
assert.include(container.innerHTML, mockContact.tel[0].type);
assert.include(
listDetails.querySelector('h2').innerHTML,
mockContact.tel[0].carrier
);
assertCarrierWrapperVisibility(true);
});
test('with 1 phone and carrier undefined', function() {
var contactNoCarrier = new MockContactAllFields(true);
contactNoCarrier.tel = [
{
value: '+34678987123',
type: ['Personal']
}
];
subject.renderPhones(contactNoCarrier);
var phoneButton = container.querySelector('#call-or-pick-0');
assert.equal(phoneButton.querySelector('b').textContent,
contactNoCarrier.tel[0].value);
var carrierContent = listDetails.querySelector('.carrier').textContent;
assert.lengthOf(carrierContent, 0);
assertCarrierWrapperVisibility(false);
});
test('with no phones', function() {
var contactWoTel = new MockContactAllFields(true);
contactWoTel.tel = [];
subject.renderPhones(contactWoTel);
assert.equal(-1, listDetails.innerHTML.indexOf('phone-details-template'));
});
test('with null phones', function() {
var contactWoTel = new MockContactAllFields(true);
contactWoTel.tel = null;
subject.renderPhones(contactWoTel);
assert.equal(-1, listDetails.innerHTML.indexOf('phone-details-template'));
});
test('with more than 1 phone', function() {
var contactMultTel = new MockContactAllFields(true);
contactMultTel.tel[1] = contactMultTel.tel[0];
for (var elem in contactMultTel.tel[1]) {
var currentElem = contactMultTel.tel[1][elem] + 'dup';
contactMultTel.tel[1][elem] = currentElem;
}
contactMultTel.tel[1].type = '';
subject.renderPhones(contactMultTel);
assert.include(listDetails.innerHTML, 'phone-details-template-0');
assert.include(listDetails.innerHTML, 'phone-details-template-1');
assert.include(listDetails.innerHTML, contactMultTel.tel[0].value);
assert.include(listDetails.innerHTML, contactMultTel.tel[0].carrier);
assert.include(listDetails.innerHTML, contactMultTel.tel[0].type);
assert.include(listDetails.innerHTML, contactMultTel.tel[1].value);
assert.include(listDetails.innerHTML, contactMultTel.tel[1].carrier);
assert.include(listDetails.innerHTML, subject.DEFAULT_TEL_TYPE);
assert.equal(
-1, listDetails.innerHTML.indexOf('phone-details-template-2'));
assertCarrierWrapperVisibility(true);
});
});
suite('Render emails', function() {
test('with 1 email', function() {
subject.renderEmails(mockContact);
assert.include(listDetails.innerHTML, 'email-details-template-0');
assert.include(listDetails.innerHTML, mockContact.email[0].value);
assert.include(listDetails.innerHTML, mockContact.email[0].type);
});
test('with no emails', function() {
var contactWoEmail = new MockContactAllFields(true);
contactWoEmail.email = [];
subject.renderEmails(contactWoEmail);
assert.equal(-1, listDetails.innerHTML.indexOf('email-details-template'));
});
test('with null emails', function() {
var contactWoEmail = new MockContactAllFields(true);
contactWoEmail.email = null;
subject.renderEmails(contactWoEmail);
assert.equal(-1, listDetails.innerHTML.indexOf('email-details-template'));
});
test('with more than 1 email', function() {
var contactMultEmail = new MockContactAllFields(true);
contactMultEmail.email[1] = contactMultEmail.email[0];
for (var elem in contactMultEmail.email[1]) {
var currentElem = contactMultEmail.email[1][elem] + 'dup';
contactMultEmail.email[1][elem] = currentElem;
}
subject.renderEmails(contactMultEmail);
assert.include(container.innerHTML, 'email-details-template-0');
assert.include(container.innerHTML, 'email-details-template-1');
var email0 = contactMultEmail.email[0];
var email1 = contactMultEmail.email[1];
assert.include(listDetails.innerHTML, email0.value);
assert.include(listDetails.innerHTML, email0.type);
assert.include(listDetails.innerHTML, email1.value);
assert.include(listDetails.innerHTML, email1.type);
assert.equal(
-1, listDetails.innerHTML.indexOf('email-details-template-2'));
});
});
suite('> Send sms', function() {
var evt = {};
evt.target.dataset.target = '+445312973212';
setup(function() {
this.sinon.spy(SmsIntegration, 'sendSms');
});
test('> send sms', function() {
subject._onSendSmsClicked(evt);
sinon.assert.calledWith(SmsIntegration.sendSms, evt);
});
});
suite('> User actions', function() {
var realMozTelephony;
var realMozMobileConnections;
suiteSetup(function() {
realMozTelephony = navigator.mozTelephony;
realMozMobileConnections = navigator.mozMobileConnections;
navigator.mozTelephony = true;
navigator.mozMobileConnections = [true];
sinon.spy(window, 'MultiSimActionButton');
});
suiteTeardown(function() {
navigator.mozTelephony = realMozTelephony;
navigator.mozMobileConnections = realMozMobileConnections;
});
setup(function() {
this.sinon.spy(LazyLoader, 'load');
});
teardown(function() {
LazyLoader.load.reset();
});
function makeCall(cb) {
var theContact = new MockContactAllFields(true);
subject.renderPhones(theContact);
var stubCall = sinon.stub(TelephonyHelper, 'call', cb);
MultiSimActionButton.args[0][1]();
stubCall.restore();
}
test('> Not loading MultiSimActionButton when we are on an activity',
function() {
ActivityHandler.currentlyHandling = true;
subject.renderPhones(mockContact);
sinon.assert.neverCalledWith(LazyLoader.load,
['/shared/js/multi_sim_action_button.js']);
ActivityHandler.currentlyHandling = false;
});
test('> Not loading MultiSimActionButton if we have a MMI code',
function() {
this.sinon.stub(subject, '_isMMI').returns(true);
subject.renderPhones(mockContact);
sinon.assert.neverCalledWith(LazyLoader.load,
['/shared/js/multi_sim_action_button.js']);
});
test('> Load call button', function() {
subject.renderPhones(mockContact);
// We have two buttons, one call per button created plus webrtc
// client call
// XXX: Should we include webrtc?
//assert.equal(LazyLoader.load.callCount, 5);
assert.equal(LazyLoader.load.callCount, 2);
var spyCall = LazyLoader.load.getCall(1);
assert.deepEqual(
['/shared/js/multi_sim_action_button.js'], spyCall.args[0]);
});
test('> Multiple MultiSimActionButtons initialized with correct values',
function() {
var theContact = new MockContactAllFields(true);
subject.renderPhones(theContact);
var phone1 = container.querySelector('#call-or-pick-0');
var phone2 = container.querySelector('#call-or-pick-1');
var phoneNumber1 = theContact.tel[0].value;
var phoneNumber2 = theContact.tel[1].value;
sinon.assert.calledWith(MultiSimActionButton, phone1,
sinon.match.func, 'ril.telephony.defaultServiceId',
sinon.match.func);
// Check the getter contains the correct phone number
var getterResult = MultiSimActionButton.args[0][3]();
assert.equal(phoneNumber1, getterResult);
sinon.assert.calledWith(MultiSimActionButton, phone2,
sinon.match.func, 'ril.telephony.defaultServiceId',
sinon.match.func);
// Second call getter result
getterResult = MultiSimActionButton.args[1][3]();
assert.equal(phoneNumber2, getterResult);
});
test('> Calling and oncall ', function() {
makeCall(function(num, cIndex, oncall, connected, disconnected, error) {
assert.isTrue(container.classList.contains('calls-disabled'));
oncall();
assert.isFalse(container.classList.contains('calls-disabled'));
});
});
test('> Calling and connected ', function() {
makeCall(function(num, cIndex, oncall, connected, disconnected, error) {
assert.isTrue(container.classList.contains('calls-disabled'));
connected();
assert.isFalse(container.classList.contains('calls-disabled'));
});
});
test('> Calling and disconnected ', function() {
makeCall(function(num, cIndex, oncall, connected, disconnected, error) {
assert.isTrue(container.classList.contains('calls-disabled'));
disconnected();
assert.isFalse(container.classList.contains('calls-disabled'));
});
});
test('> Calling and error ', function() {
makeCall(function(num, cIndex, oncall, connected, disconnected, error) {
assert.isTrue(container.classList.contains('calls-disabled'));
error();
assert.isFalse(container.classList.contains('calls-disabled'));
});
});
});
});

View file

@ -0,0 +1,936 @@
'use strict';
/* global contactFB:true */
/* global MockFindMatcher */
/* global Matcher */
require('/shared/js/text_normalizer.js');
require('/shared/js/simple_phone_matcher.js');
require('/shared/js/contacts/contacts_matcher.js');
requireApp('communications/contacts/test/unit/mock_find_matcher.js');
var realmozContacts,
contact;
if (!window.realmozContacts) {
window.realmozContacts = null;
}
if (!window.contact) {
window.contact = null;
}
if (!window.contactFB) {
window.contactFB = null;
}
if (!window.SimplePhoneMatcher) {
window.SimplePhoneMatcher = null;
}
suite('Test Contacts Matcher', function() {
function assertDefaultMatch(results, matchingFields, telValue) {
assert.equal(Object.keys(results).length, 1);
assert.equal(Object.keys(results)[0], '1B');
var matchingContact = results['1B'].matchingContact;
if (Array.isArray(matchingContact.email) && matchingContact.email[0]) {
assert.equal(matchingContact.email[0].value, 'jj@jj.com');
}
if (Array.isArray(matchingContact.tel) && matchingContact.tel[0]) {
assert.equal(matchingContact.tel[0].value, telValue || '676767671');
}
if (matchingFields) {
matchingFields.forEach(function(matchingField) {
if (typeof matchingField === 'string') {
assert.isDefined(results['1B'].matchings[matchingField]);
}
else if (matchingField && typeof matchingField === 'object') {
var matchArr = results['1B'].matchings[matchingField.field];
assert.isDefined(matchArr);
assert.lengthOf(matchArr, matchingField.times);
}
});
}
}
function testMatch(myObj, mode, matchingFields, done, telValue) {
var cbs = {
onmatch: function(results) {
assertDefaultMatch(results, matchingFields, telValue);
done();
},
onmismatch: function() {
assert.fail(cbs.onmatch, cbs.onmismatch, 'No contacts matches found!!');
done();
}
};
Matcher.match(myObj, mode, cbs);
}
function testMismatch(myObj, mode, done) {
var cbs = {
onmatch: function(results) {
assert.fail(cbs.onmismatch, cbs.onmatch, 'Matching found!!');
done();
},
onmismatch: function() {
assert.ok(true, 'Mismatch callback invoked');
done();
}
};
Matcher.match(myObj, mode, cbs);
}
function setupSuite() {
contact = {
id: '1B',
givenName: ['Carlos'],
familyName: ['Álvarez del Río'],
tel: [{
type: ['home'],
value: '676767671'
}],
email: [{
type: ['personal'],
value: 'jj@jj.com'
}]
};
MockFindMatcher.setData(contact);
realmozContacts = navigator.mozContacts;
navigator.mozContacts = MockFindMatcher;
Matcher.dataProvider = navigator.mozContacts;
}
suiteTeardown(function() {
navigator.mozContacts = realmozContacts;
});
suite('Test Contacts Matcher. PassiveMode', function() {
suiteSetup(setupSuite);
test('Matching by name and phone number', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.email = [];
testMatch(myObj, 'passive', null, done);
});
test('Matching by name and phone number. Accents', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.email = [];
myObj.familyName = ['Alvarez delrio'];
testMatch(myObj, 'passive', null, done);
});
test('Matching by name and phone number. International number incoming',
function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.tel = [{
type: contact.tel[0].type,
value: '+34' + contact.tel[0].value
}];
myObj.email = [];
myObj.familyName = ['Alvarez delrio'];
testMatch(myObj, 'passive', null, done);
});
test('Matching by name and phone number. International number existing',
function(done) {
var myObj = Object.create(contact);
var savedContactTel = contact.tel;
contact.tel = [{
type: ['mobile'],
value: '+34' + savedContactTel[0].value
}];
myObj.id = '1A';
myObj.tel = [{
type: savedContactTel[0].type,
value: savedContactTel[0].value
}];
myObj.email = [];
myObj.familyName = ['Alvarez delrio'];
testMatch(myObj, 'passive', null, function() {
contact.tel = savedContactTel;
done();
}, contact.tel[0].value);
});
test('Matching by name and phone number. Name defined in the name prop',
function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.email = [];
myObj.familyName = [];
myObj.givenName = ['Carlos Alvarez del Río'];
myObj.name = ['Carlos Alvarez del Río'];
var saveGN = contact.givenName;
var saveFN = contact.familyName;
contact.givenName = ['Carlos Alvarez del Río'];
contact.familyName = [' '];
contact.name = ['Carlos Álvarez del rio'];
testMatch(myObj, 'passive', null, function() {
contact.givenName = saveGN;
contact.familyName = saveFN;
contact.name = null;
done();
});
});
test('Matching by name and e-mail', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.tel = null;
testMatch(myObj, 'passive', null, done);
});
test('Matching by name, e-mail and phone number', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
testMatch(myObj, 'passive', null, done);
});
test('Phone number matches but name does not match', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.givenName = ['Adolfo'];
myObj.email = null;
testMismatch(myObj, 'passive', done);
});
test('e-mail matches but name does not match', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.givenName = ['Adolfo'];
myObj.tel = [];
testMismatch(myObj, 'passive', done);
});
test('Name matches, e-mail matches but phone number does not match',
function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.tel = [{
type: ['mobile'],
value: '98645678'
}];
testMismatch(myObj, 'passive', done);
});
test('Name matches, phone number matches but e-mail does not match',
function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.email = [{
type: ['personal'],
value: 'kk@kk.com'
}];
testMismatch(myObj, 'passive', done);
});
test('First name is null only in incoming, lastName matches. Then mismatch',
function(done) {
var myObj = Object.create(contact);
myObj.id = 'abcde';
myObj.givenName = [];
testMismatch(myObj, 'passive', done);
});
test('First name is null only in existing contact. Then mismatch',
function(done) {
var myObj = Object.create(contact);
myObj.id = 'wvy';
myObj.givenName = ['Carlos'];
var saveGN = contact.givenName;
contact.givenName = null;
testMismatch(myObj, 'passive', function() {
contact.givenName = saveGN;
done();
});
});
test('First name is null, in both. LastName matches. Then match',
function(done) {
var myObj = Object.create(contact);
myObj.id = 'zx1';
myObj.givenName = [];
var saveGN = contact.givenName;
contact.givenName = [];
testMatch(myObj, 'passive', null, function() {
contact.givenName = saveGN;
done();
});
});
test('Last name is null, in both. First name matches. Then match',
function(done) {
var myObj = Object.create(contact);
myObj.id = 'zx1';
myObj.familyName = [];
var saveFN = contact.familyName;
contact.familyName = [];
testMatch(myObj, 'passive', null, function() {
contact.familyName = saveFN;
done();
});
});
test('Match by name and phone number when existing contact is FB Linked',
function(done) {
var linkedContact = {
id: '1B',
category: ['facebook', 'fb_linked', '678910'],
givenName: ['Jose'],
familyName: ['Mota'],
name: ['Jose Mota'],
tel: [
{
type: ['mobile'],
value: '676767671'
}
]
};
var incomingContact = {
id: 'kjh987',
category: ['gmail'],
givenName: linkedContact.givenName,
familyName: linkedContact.familyName,
name: linkedContact.name,
tel: [
{
type: ['mobile'],
value: '676767671'
},
{
type: ['personal'],
value: '983456789'
}
]
};
MockFindMatcher.setData(linkedContact);
testMatch(incomingContact, 'passive', null, function() {
MockFindMatcher.setData(contact);
done();
});
});
suite('SIM Contacts Matching', function() {
var existingSimContact = {
id: '1B',
category: ['sim'],
name: ['Juan Ramón del SIM'],
givenName: ['Juan Ramón del SIM'],
tel: [{
type: ['home'],
value: '676767671'
}],
email: [{
type: ['personal'],
value: 'jj@jj.com'
}]
};
var incomingSimContact = {
id: '678',
category: ['sim'],
givenName: ['Juan Ramon'],
familyName: ['del SIM'],
name: ['Juan Ramón del SIM'],
tel: [{
type: ['home'],
value: '676767671'
}]
};
test('Incoming SIM Contact matches with a normal contact',
function(done) {
MockFindMatcher.setData(existingSimContact);
testMatch(incomingSimContact, 'passive', null, function() {
MockFindMatcher.setData(contact);
done();
});
});
test('Incoming Contact matches with an existing SIM contact',
function(done) {
MockFindMatcher.setData(existingSimContact);
testMatch(incomingSimContact, 'passive', null, function() {
MockFindMatcher.setData(contact);
done();
});
});
test('SIM Contact. Matching by name and SIM url', function(done) {
var existing = Object.create(existingSimContact);
existing.givenName = ['Álvaro'];
existing.familyName = ['Fummetta'];
existing.name = ['Álvaro Fummetta'];
existing.url = [
{
type: ['sim', 'source'],
value: '12345-6789'
}
];
// Avoiding matching by other criteria
existing.tel = null;
existing.email = null;
var incoming = Object.create(incomingSimContact);
incoming.url = existing.url;
incoming.name = existing.name;
incoming.givenName = existing.givenName;
incoming.familyName = existing.familyName;
MockFindMatcher.setData(existing);
testMatch(incoming, 'passive', null, function() {
MockFindMatcher.setData(contact);
done();
});
}); // Test
test('SIM Contact. Matching by name but SIM url different',
function(done) {
var existing = Object.create(existingSimContact);
existing.url = [
{
type: ['sim', 'source'],
value: '12345-6789'
}
];
// Avoiding matching by other criteria
existing.tel = null;
existing.email = null;
var incoming = Object.create(incomingSimContact);
incoming.url = [
{
type: ['sim', 'source'],
value: '6666-9999'
}
];
incoming.name = existing.name;
incoming.givenName = existing.givenName;
incoming.familyName = existing.familyName;
MockFindMatcher.setData(existing);
testMismatch(incoming, 'passive', function() {
MockFindMatcher.setData(contact);
done();
});
});
test('SIM Contact. Multiple names matching. Only one by SIM url',
function(done) {
var existing1 = Object.create(existingSimContact);
existing1.url = [
{
type: ['sim', 'source'],
value: '12345-6789'
}
];
// Avoiding matching by other criteria
existing1.tel = null;
existing1.email = null;
var incoming = Object.create(incomingSimContact);
incoming.url = [
{
type: ['sim', 'source'],
value: '6666-9999'
}
];
incoming.name = existing1.name;
incoming.givenName = existing1.givenName;
incoming.familyName = existing1.familyName;
var existing2 = Object.create(incoming);
existing2.id = 'vtrew12';
MockFindMatcher.setData(null);
MockFindMatcher.setFoundData([existing1, existing2]);
var callbacks = {
onmatch: function(results) {
var resultList = Object.keys(results);
assert.equal(resultList.length, 1);
assert.equal(results[resultList[0]].matchingContact.id,
existing2.id);
MockFindMatcher.setData(contact);
MockFindMatcher.setFoundData(null);
done();
},
onmismatch: function() {
done('error');
}
};
Matcher.match(incoming, 'passive', callbacks);
}); // Test
}); // SIM Suite
}); // Passive Merge Suite
suite('Test Contacts Matcher. Active Mode', function() {
suiteSetup(setupSuite);
test('Matching by name', function(done) {
var existingContact = {
id: '1B',
givenName: ['Jander'],
familyName: ['Klander'],
name: ['Jander Klander']
};
var updatedContact = {
id: '1B',
givenName: ['Jander'],
familyName: ['Klander'],
name: ['Jander Klander'],
org: ['Company']
};
MockFindMatcher.setData(existingContact);
testMismatch(updatedContact, 'active', function() {
MockFindMatcher.setData(contact);
done();
});
});
test('Matching by phone', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.givenName = [];
myObj.familyName = null;
testMatch(myObj, 'active', ['tel'], done);
});
test('Matching by phone internationalized number', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.givenName = [];
myObj.familyName = null;
myObj.tel = [];
myObj.tel[0] = {
type: ['mobile'],
value: '+34' + contact.tel[0].value
};
testMatch(myObj, 'active', ['tel'], done);
});
test('Matching by phone internationalized number existing', function(done) {
var savedTel = contact.tel;
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.givenName = [];
myObj.familyName = null;
myObj.tel = [];
myObj.tel[0] = {
type: ['mobile'],
value: savedTel[0].value
};
contact.tel = [{
type: savedTel[0].type,
value: '0034' + savedTel[0].value
}];
testMatch(myObj, 'active', ['tel'], function() {
contact.tel = savedTel;
done();
}, contact.tel[0].value);
});
test('Matching by multiple phones', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
var savedTel = contact.tel;
contact.tel = [
savedTel[0],
{
type: ['home'],
value: '67890123'
}
];
myObj.givenName = [];
myObj.familyName = null;
testMatch(myObj, 'active', [{field: 'tel', times: 2}], function() {
contact.tel = savedTel;
done();
});
});
test('Matching by email', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.tel = [{
type: ['personal'],
value: '9999999'
}];
myObj.givenName = ['Lucas'];
myObj.familyName = ['Petrov'];
testMatch(myObj, 'active', ['email'], done);
});
test('Matching by multiple emails', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
var savedEmail = contact.email;
contact.email = [
savedEmail[0],
{
type: ['work'],
value: 'k1zq@example.com'
}];
myObj.givenName = [];
myObj.familyName = null;
testMatch(myObj, 'active', [{field: 'email', times: 2}], function() {
contact.email = savedEmail;
done();
});
});
test('Matching by phone and email', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.givenName = [' '];
myObj.familyName = [' '];
testMatch(myObj, 'active', ['tel', 'email'], done);
});
test('Matching by name', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.email = [];
myObj.tel = [{
type: ['personal'],
value: '9999999'
}];
testMatch(myObj, 'active', ['name'], done);
});
test('SIM Contact. Name is totally empty and there is tel. No matches',
function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.name = myObj.givenName = myObj.familyName = [];
myObj.category = ['sim'];
myObj.tel = [{
type: ['personal'],
value: '9999999'
}];
myObj.email = null;
testMismatch(myObj, 'active', done);
});
test('Regular Contact. Name is totally empty and there is tel. No matches',
function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.name = myObj.givenName = myObj.familyName = [];
myObj.category = null;
myObj.tel = [{
type: ['personal'],
value: '9999999'
}];
myObj.email = null;
testMismatch(myObj, 'active', done);
});
test('Matching by phone, email and name', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
testMatch(myObj, 'active', ['tel', 'email', 'name'], done);
});
test('Matching by name. givenName startsWith', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.email = [];
myObj.tel = null;
myObj.givenName = ['Carlos Ángel'];
testMatch(myObj, 'active', ['name'], done);
});
test('familyName matches but givenName not. Mismatch', function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.email = [];
myObj.tel = null;
myObj.givenName = ['David'];
testMismatch(myObj, 'active', done);
});
test('familyName of incoming is empty. GivenName matches. Mismatch',
function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.email = [];
myObj.tel = null;
myObj.familyName = null;
testMismatch(myObj, 'active', done);
});
test('givenName of incoming is empty. familyName matches. Mismatch',
function(done) {
var myObj = Object.create(contact);
myObj.id = '1A';
myObj.email = [];
myObj.tel = null;
myObj.givenName = null;
testMismatch(myObj, 'active', done);
});
test('family Name of existing is empty. givenName Matches. Mismatch',
function(done) {
var myObj = Object.create(contact);
myObj.id = 'zxy';
myObj.email = null;
myObj.tel = null;
myObj.familyName = contact.familyName;
contact.familyName = [];
testMismatch(myObj, 'active', function() {
contact.familyName = myObj.familyName;
done();
});
});
test('given Name of existing is empty. family Name Matches. Mismatch',
function(done) {
var myObj = Object.create(contact);
myObj.id = 'zxy';
myObj.email = null;
myObj.tel = null;
myObj.givenName = contact.givenName;
contact.givenName = null;
testMismatch(myObj, 'active', function() {
contact.givenName = myObj.givenName;
done();
});
});
test('Both familyNames are empty. Given names match. Mismatch',
function(done) {
var myObj = Object.create(contact);
myObj.id = 'zxy';
myObj.email = null;
myObj.tel = null;
var saveFN = contact.familyName;
myObj.familyName = contact.familyName = [null];
testMismatch(myObj, 'active', function() {
contact.familyName = saveFN;
done();
});
});
test('Both givenNames are empty. Family names match. Mismatch',
function(done) {
var myObj = Object.create(contact);
myObj.id = 'zxy';
myObj.email = null;
myObj.tel = null;
var saveGN = contact.givenName;
myObj.givenName = contact.givenName = [''];
testMismatch(myObj, 'active', function() {
contact.givenName = saveGN;
done();
});
});
test('Incoming SIM Contact matches with a normal contact', function(done) {
var simObj = {
id: '678',
category: ['sim'],
name: ['Juan Ramón del SIM'],
givenName: ['Juan Ramón del SIM']
};
var existingContact = {
id: '1B',
givenName: ['Juan Ramon'],
familyName: ['del SIM'],
name: ['Juan Ramón del SIM'],
tel: [{
type: ['home'],
value: '676767671'
}],
email: [{
type: ['personal'],
value: 'jj@jj.com'
}]
};
MockFindMatcher.setData(existingContact);
testMatch(simObj, 'active', ['name'], function() {
MockFindMatcher.setData(contact);
done();
});
});
test('Incoming Contact matches with an existing SIM contact',
function(done) {
var existingSimContact = {
id: '1B',
category: ['sim'],
name: ['Juan Ramón del SIM'],
givenName: ['Juan Ramón del SIM'],
tel: [{
type: ['home'],
value: '676767671'
}],
email: [{
type: ['personal'],
value: 'jj@jj.com'
}]
};
var incomingContact = {
id: '678',
givenName: ['Juan Ramon'],
familyName: ['del SIM'],
name: ['Juan Ramón del SIM']
};
MockFindMatcher.setData(existingSimContact);
testMatch(incomingContact, 'active', ['name'], function() {
MockFindMatcher.setData(contact);
done();
});
});
test('No matches at all', function(done) {
var myObj = {
id: '1A',
email: [{}],
tel: [{
type: ['mobile'],
value: '8888888'
}],
givenName: null,
familyName: []
};
testMismatch(myObj, 'active', done);
});
});
suite('Test Contacts Matcher. Active Mode. Facebook Contacts', function() {
suiteSetup(function() {
contactFB = {
id: '1B',
givenName: ['Carlos'],
familyName: ['Álvarez del Río'],
tel: [{
type: ['home'],
value: '676767671'
}],
email: [{
type: ['personal'],
value: 'jj@jj.com'
}],
category: ['facebook']
};
MockFindMatcher.setData(contactFB);
realmozContacts = navigator.mozContacts;
navigator.mozContacts = MockFindMatcher;
});
test('A Facebook Imported Contact never matches', function(done) {
var contactObj = Object.create(contact);
contactObj.id = '9876';
contactObj.category = null;
testMismatch(contactObj, 'active', done);
});
test('A Facebook linked Contact can match', function(done) {
var contactObj = Object.create(contactFB);
contactObj.id = '9876';
contactFB.category = ['facebook', 'fb_linked', '123456789'];
contactObj.category = [];
testMatch(contactObj, 'active', ['tel', 'email'], done);
});
test('Two FB linked Contacts can match if they link the same Friend',
function(done) {
var contactObj = Object.create(contactFB);
contactObj.id = '9876';
testMatch(contactObj, 'active', ['tel', 'email'], done);
});
test('A FB linked Contact cannot match with a Contact linked to another',
function(done) {
var contactObj = Object.create(contactFB);
contactObj.id = 'xc9876';
contactObj.category = ['facebook', 'fb_linked', '987654321'];
testMismatch(contactObj, 'active', done);
});
});
});

View file

@ -0,0 +1,138 @@
'use strict';
/* exported dupContacts */
/* exported matchingDetailsData */
var CORRECT_MATCHED_VALUE = '$correct$';
var dataImage = 'data:image/gif;base64,R0lGODlhyAAiALM...DfD0QAADs=';
var dupContacts = {
'1a': {
matchingContact: {
id: '1a',
givenName: ['Manolo'],
familyName: ['García'],
photo: [dataImage],
email: [{
value: 'man@tid.es'
}]
},
matchings: {
'name': [{
target: 'Manolo',
matchedValue: CORRECT_MATCHED_VALUE
}]
}
},
'2b': {
matchingContact: {
id: '2b',
givenName: ['Manolo'],
familyName: ['García'],
photo: [dataImage],
email: [{
value: 'man@tid.es'
}, {
value: 'man@telefonica.es'
}]
},
matchings: {
'name': [{
target: 'Manolo',
matchedValue: 'Manolo Garcia'
}],
'email': [{
target: 'man@tid.es',
matchedValue: CORRECT_MATCHED_VALUE
}]
}
},
'3c': {
matchingContact: {
id: '3c',
givenName: ['Manolo'],
familyName: ['García'],
photo: [dataImage],
email: [{
value: 'man@tid.es'
}],
'tel': [{
value: '+346578888881',
type: 'Mobile'
}]
},
matchings: {
'name': [{
target: 'Manolo',
matchedValue: 'Manolo Garcia'
}],
'email': [{
target: 'man@tid.es',
matchedValue: 'man@tid.es'
}],
'tel': [{
target: '+346578888881',
matchedValue: CORRECT_MATCHED_VALUE
}]
}
}
};
var matchingDetailsData = {
'user_id_1': {
matchingContact: {
id: 'user_id_1',
name: ['The Name The Surname'],
givenName: ['The Name'],
familyName: ['The Surname'],
photo: [],
email: [{
value: 'email_1@acme.com',
type: 'email_type_1'
}, {
value: 'email_2@acme.com',
type: 'email_type_2'
}],
'tel': [{
value: '111111111',
type: 'type_1'
}, {
value: '222222222',
type: 'type_2'
}]
},
matchings: {
'name': [{
target: 'The Name The Surname',
matchedValue: 'The Name The Surname'
}],
'tel': [{
target: '222222222',
matchedValue: '222222222'
}],
'email': [{
target: 'email_2@acme.com',
matchedValue: 'email_2@acme.com'
}]
}
},
'user_id_2': {
matchingContact: {
id: 'user_id_2',
name: ['The Name Another Surname'],
givenName: ['The Name'],
familyName: ['Another Surname'],
photo: [],
'tel': [{
value: '111111111',
type: 'type_1'
}]
},
matchings: {
'tel': [{
target: '111111111',
matchedValue: 'type_1'
}]
}
}
};

View file

@ -0,0 +1,719 @@
/* globals contacts, MockFindMatcher, utils, MocksHelper, MockThumbnailImage */
'use strict';
require('/shared/js/simple_phone_matcher.js');
require('/shared/js/contacts/import/utilities/misc.js');
require('/shared/js/contacts/contacts_merger.js');
requireApp('communications/contacts/test/unit/mock_find_matcher.js');
requireApp('communications/contacts/test/unit/mock_image_thumbnail.js');
require('/shared/test/unit/mocks/mock_contact_photo_helper.js');
require('/shared/test/unit/mocks/mock_moz_contact.js');
var mocksHelperForContactsMerger = new MocksHelper([
'ContactPhotoHelper',
'mozContact'
]).init();
suite('Contacts Merging Tests', function() {
mocksHelperForContactsMerger.attachTestHelpers();
var toMergeContacts = null,
toMergeContact = null,
realmozContacts = null,
realThumbnailImage = null;
var aPhoto1 = new Blob(['123456789'], {type: 'image/png'});
var aPhoto2 = new Blob(['98786'], {type: 'image/jpg'});
function MasterContact() {
this.id = '1A';
this.givenName = ['Alfred'];
this.familyName = ['Müller'];
this.tel = [{
type: ['work'],
value: '67676767'
}];
this.email = [{
type: ['work'],
value: 'jj@jj.com'
}];
this.org = ['Müller & Co'];
this.adr = [{
type: ['work'],
streetAddress: 'Friedrich',
locality: 'Madrid',
region: 'Madrid',
postalCode: '28009',
countryName: 'Spanien'
}];
this.note = [
'Ingenieur'
];
}
suiteSetup(function() {
toMergeContacts = [{
matchingContact: null,
matchings: {}
}
];
toMergeContact = toMergeContacts[0];
realmozContacts = navigator.mozContacts;
navigator.mozContacts = MockFindMatcher;
realThumbnailImage = utils.thumbnailImage;
utils.thumbnailImage = MockThumbnailImage;
});
suiteTeardown(function() {
navigator.mozContacts = realmozContacts;
utils.thumbnailImage = realThumbnailImage;
});
function assertFieldValues(field, values, property) {
values.forEach(function(aValue) {
var val = field.filter(function(x) {
var testVal = x.value || x;
if (property) {
testVal = x[property][0];
}
return testVal === aValue;
});
assert.lengthOf(val, 1);
});
}
function assertPhoto(photo, target) {
assert.isTrue(photo.size === target.size);
assert.isTrue(photo.type === target.type);
}
test('Merge first name and last name. First name prefix', function(done) {
toMergeContact.matchingContact = {
givenName: ['Alfred Albert'],
familyName: ['Müller']
};
contacts.Merger.merge(new MasterContact(), toMergeContacts, {
success: function(result) {
assert.equal(result.givenName[0], 'Alfred');
assert.equal(result.givenName[1], 'Alfred Albert');
assert.equal(result.familyName[0], 'Müller');
done();
}});
});
test('Merge accepts matching results without the `matchings` field',
function(done) {
toMergeContact.matchingContact = {
givenName: ['Alfred Albert'],
familyName: ['Müller']
};
delete toMergeContact.matchings;
contacts.Merger.merge(new MasterContact(), toMergeContacts, {
success: function(result) {
assert.equal(result.givenName[0], 'Alfred');
assert.equal(result.givenName[1], 'Alfred Albert');
assert.equal(result.familyName[0], 'Müller');
done();
}});
});
test('Merge first name and last name. incoming names empty', function(done) {
toMergeContact.matchingContact = {
givenName: [],
familyName: [],
tel: [{
type: ['work'],
value: '67676767'
}]
};
contacts.Merger.merge(new MasterContact(), toMergeContacts, {
success: function(result) {
assert.equal(result.givenName[0], 'Alfred');
assert.equal(result.familyName[0], 'Müller');
done();
}});
});
test('Merge first name and last name. existing names empty', function(done) {
toMergeContact.matchingContact = {
givenName: ['Alfred'],
familyName: ['Müller von Bismarck'],
tel: [{
type: ['work'],
value: '67676767'
}]
};
var masterContact = new MasterContact();
masterContact.givenName = null;
masterContact.familyName = null;
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.equal(result.givenName[0], 'Alfred');
assert.equal(result.familyName[0], 'Müller von Bismarck');
done();
}});
});
test('Merge first name and last name. existing and incoming given names' +
'empty', function(done) {
toMergeContact.matchingContact = {
givenName: [],
familyName: ['Müller von Bismarck'],
tel: [{
type: ['work'],
value: '67676767'
}]
};
var masterContact = new MasterContact();
masterContact.givenName = null;
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.isTrue(result.givenName.length === 0);
assert.equal(result.familyName[0], 'Müller');
done();
}});
});
test('Merge first name and last name. existing and incoming last names empty',
function(done) {
toMergeContact.matchingContact = {
givenName: ['Alfred Albert'],
familyName: [],
tel: [{
type: ['work'],
value: '67676767'
}]
};
var masterContact = new MasterContact();
masterContact.familyName = null;
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.isTrue(result.familyName.length === 0);
assert.equal(result.givenName[0], 'Alfred');
done();
}});
});
test('Merging existing with only familyName with an incoming SIM Contact',
function(done) {
var masterContact = new MasterContact();
masterContact.givenName[0] = '';
masterContact.name = [];
masterContact.familyName = ['Smith'];
masterContact.name[0] = masterContact.familyName[0];
toMergeContact.matchingContact = {
givenName: ['Smith'],
familyName: [],
name: ['Smith'],
category: ['sim']
};
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.isTrue(!result.givenName[0]);
assert.equal(result.familyName[0], 'Smith');
assert.equal(result.name[0], 'Smith');
done();
}
});
});
test('Merging existing with only givenName with an incoming SIM Contact',
function(done) {
var masterContact = new MasterContact();
masterContact.givenName[0] = 'Lionel';
masterContact.name = [];
masterContact.familyName = null;
masterContact.name[0] = masterContact.givenName[0];
toMergeContact.matchingContact = {
givenName: ['Lionel'],
name: ['Lionel'],
category: ['sim']
};
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.isTrue(!result.familyName[0]);
assert.equal(result.givenName[0], 'Lionel');
assert.equal(result.name[0], 'Lionel');
done();
}
});
});
test('Merging a complete existing with an incoming SIM Contact',
function(done) {
var masterContact = new MasterContact();
toMergeContact.matchingContact = {
givenName: ['Alfred'],
name: ['Müller'],
category: ['sim']
};
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.equal(masterContact.familyName[0], result.familyName[0]);
assert.equal(masterContact.givenName[0], result.givenName[0]);
assert.equal(masterContact.name[0], result.name[0]);
done();
}
});
});
test('Merge telephone numbers. Adding a new one', function(done) {
toMergeContact.matchingContact = {
tel: [{
type: ['work'],
value: '67676767'
},
{
type: null,
value: '0987654'
}]
};
contacts.Merger.merge(new MasterContact(), toMergeContacts, {
success: function(result) {
assert.lengthOf(result.tel, 2);
// email is left untouched
assert.lengthOf(result.email, 1);
assertFieldValues(result.tel, ['67676767', '0987654']);
assertFieldValues(result.tel, ['work', 'other'], 'type');
done();
}});
});
test('Merge telephone numbers. Leaving as they are', function(done) {
toMergeContact.matchingContact = {
tel: [{
type: ['work'],
value: '67676767'
}]
};
var masterContact = new MasterContact();
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.tel, 1);
assertFieldValues(result.tel, [masterContact.tel[0].value]);
done();
}});
});
test('Merge international telephone numbers with local ones', function(done) {
toMergeContacts[0] = {
matchingContact: {
tel: [{
type: ['work'],
value: '+3467676767'
}]
},
matchings: {
'tel': [
{
target: '67676767',
matchedValue: '+3467676767'
}
]
}
};
var masterContact = new MasterContact();
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.tel, 1);
assertFieldValues(result.tel, [masterContact.tel[0].value]);
// Restoring
toMergeContacts[0] = toMergeContact;
done();
}});
});
test('Merge tel numbers with extra characters and without', function(done) {
toMergeContacts[0] = {
matchingContact: {
tel: [{
type: ['work'],
value: '(67)-67. 67 67-()'
}]
},
matchings: {
'tel': [
{
target: '67676767',
matchedValue: '(67)-67. 67 67-()'
}
]
}
};
var masterContact = new MasterContact();
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.tel, 1);
assertFieldValues(result.tel, [masterContact.tel[0].value]);
// Restoring
toMergeContacts[0] = toMergeContact;
done();
}});
});
test('Merge emails. Adding a new one', function(done) {
toMergeContact.matchingContact = {
email: [{
type: [],
value: 'home@sweet.home'
}]
};
contacts.Merger.merge(new MasterContact(), toMergeContacts, {
success: function(result) {
assert.lengthOf(result.email, 2);
// tel is left untouched
assert.lengthOf(result.tel, 1);
assertFieldValues(result.email, ['jj@jj.com', 'home@sweet.home']);
assertFieldValues(result.email, ['work', 'other'], 'type');
done();
}});
});
test('Merge emails. Leaving as they are', function(done) {
toMergeContact.matchingContact = {
email: [{
type: ['work'],
value: 'jj@jj.com'
}]
};
var masterContact = new MasterContact();
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.email, 1);
assertFieldValues(result.email, [masterContact.email[0].value]);
done();
}});
});
test('Merge emails. Leaving as they are. Capital Letters', function(done) {
toMergeContact.matchingContact = {
email: [{
type: ['work'],
value: 'JJ@jj.com'
}]
};
var masterContact = new MasterContact();
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.email, 1);
assertFieldValues(result.email, [masterContact.email[0].value]);
done();
}});
});
test('Merge comments', function(done) {
toMergeContact.matchingContact = {
tel: [{
type: ['work'],
value: '67676767'
}],
note: ['Another comment for him']
};
var masterContact = new MasterContact();
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.note, 2);
assertFieldValues(result.note, [toMergeContact.matchingContact.note[0],
masterContact.note[0]]);
done();
}});
});
test('Merge addresses', function(done) {
toMergeContact.matchingContact = {
tel: [{
type: ['work'],
value: '67676767'
}],
adr: [{
type: null,
streetAddress: 'Gran Via',
locality: 'Madrid',
region: 'Madrid',
postalCode: '28100',
countryName: 'España'
}]
};
contacts.Merger.merge(new MasterContact(), toMergeContacts, {
success: function(result) {
assert.lengthOf(result.adr, 2);
assertFieldValues(result.adr, ['work', 'home'], 'type');
done();
}});
});
test('Merge companies. Existing has company', function(done) {
toMergeContact.matchingContact = {
tel: [{
type: ['work'],
value: '67676767'
}],
org: ['ONG and Assoc.']
};
var masterContact = new MasterContact();
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.org, 1);
assertFieldValues(result.org, [masterContact.org[0]]);
done();
}});
});
test('Merge companies. Existing has no company', function(done) {
toMergeContact.matchingContact = {
tel: [{
type: ['work'],
value: '67676767'
}],
org: ['ONG and Assoc.']
};
var masterContact = new MasterContact();
masterContact.org = null;
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.org, 1);
assertFieldValues(result.org, [toMergeContact.matchingContact.org[0]]);
done();
}});
});
test('Merge photo. To merge has photo. Master not', function(done) {
toMergeContact.matchingContact = {
tel: [{
type: ['work'],
value: '67676767'
}],
photo: [aPhoto1]
};
contacts.Merger.merge(new MasterContact(), toMergeContacts, {
success: function(result) {
assert.lengthOf(result.photo, 1);
assertPhoto(result.photo[0],
toMergeContact.matchingContact.photo[0]);
done();
}});
});
test('Merge photo. Master has photo. To merge not', function(done) {
toMergeContact.matchingContact = {
tel: [{
type: ['work'],
value: '67676767'
}],
};
var masterContact = new MasterContact();
masterContact.photo = [aPhoto2]; // Different photo
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.photo, 1);
assertPhoto(result.photo[0], masterContact.photo[0]);
done();
}});
});
test('Merge photo. Both have photo', function(done) {
toMergeContact.matchingContact = {
tel: [{
type: ['work'],
value: '67676767'
}],
photo: [aPhoto1]
};
var masterContact = new MasterContact();
masterContact.photo = [aPhoto2]; // Different photo
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.photo, 1);
assertPhoto(result.photo[0], masterContact.photo[0]);
done();
}});
});
test('Merge Categories', function(done) {
toMergeContact.matchingContact = {
category: ['category1']
};
var masterContact = new MasterContact();
masterContact.category = ['category1', 'category2'];
contacts.Merger.merge(masterContact, toMergeContacts, {
success: function(result) {
assert.lengthOf(result.category, 2);
assertFieldValues(result.category, masterContact.category);
done();
}});
});
test('Multiple merges', function(done) {
toMergeContacts.push({
matchingContact: {
id: '1B',
givenName: ['Alfred'],
familyName: ['Müller'],
tel: [{
type: ['mobile'],
value: '3456789'
},
{
type: ['mobile'],
value: '777777'
}],
email: [{
type: ['personal'],
value: 'personal@example.com'
},
{
type: ['personal'],
value: 'jj@jj.com'
}],
note: [
'Another note'
],
photo: [aPhoto1]
},
matchings: {}
});
toMergeContact.matchingContact = {
givenName: ['Alfred'],
familyName: ['Müller'],
tel: [{
type: ['mobile'],
value: '3456789'
}]
};
contacts.Merger.merge(new MasterContact(), toMergeContacts, {
success: function(result) {
assertFieldValues(result.givenName, ['Alfred']);
assertFieldValues(result.familyName, ['Müller']);
assert.lengthOf(result.tel, 3);
assertFieldValues(result.tel, ['3456789', '777777', '67676767']);
assert.lengthOf(result.email, 2);
assertFieldValues(result.email, ['jj@jj.com', 'personal@example.com']);
assert.lengthOf(result.adr, 1);
assert.lengthOf(result.note, 2);
assert.lengthOf(result.photo, 1);
assertPhoto(result.photo[0], aPhoto1);
assert.lengthOf(result.org, 1);
assertFieldValues(result.org, ['Müller & Co']);
done();
}});
});
test('Merge duplicated contact', function(done) {
var contactToMerge = new MasterContact();
contacts.Merger.merge(
new MasterContact(),
[
{
matchingContact: contactToMerge
}
], {
success: function(result) {
// mozContacts fields that are not filled in main contact will be
// filled with 'undefined' what makes no difference for how the app
// works, but we cannot just compare (deepEqually) MasterContact to
// the result contact.
for (var key in contactToMerge) {
// Some of the fields are Arrays and we want to be sure they are
// equal. deepEqual works as equal for simple literals so it makes
// no difference for us here.
assert.deepEqual(result[key], contactToMerge[key]);
}
done();
}});
});
});

View file

@ -0,0 +1,70 @@
'use strict';
/* global MockSelectionDom */
/* global triggerEvent */
require('/shared/js/contacts/utilities/dom.js');
requireApp('communications/contacts/test/unit/helper.js');
requireApp('communications/contacts/test/unit/mock_selection_dom.js.html');
requireApp('communications/contacts/js/contacts_tag.js');
suite('Fill tag options', function() {
var subject;
suiteSetup(function() {
subject = window.ContactsTag;
document.body.innerHTML = MockSelectionDom;
});
suiteTeardown(function() {
document.body.innerHTML = '';
});
suite('go to selected tag', function() {
var container,
customTag,
originalTag;
var testTagOptions = {
'test-type' : [
{type: 'value1', value: 'value1'},
{type: 'value2', value: 'value2'}
]
};
setup(function() {
container = document.getElementById('tags-list');
customTag = document.getElementById('custom-tag');
originalTag = document.createElement('button');
originalTag.dataset.value = 'value1';
subject.setCustomTag(customTag);
customTag.addEventListener('touchend', subject.touchCustomTag);
});
test('render tag selection form', function() {
subject.fillTagOptions(container, originalTag,
testTagOptions['test-type']);
assert.equal(container.querySelector('button[data-index="0"]')
.getAttribute('data-l10n-id'), 'value1');
assert.equal(container.querySelector('button[data-index="1"]')
.getAttribute('data-l10n-id'), 'value2');
});
test('choose a tag', function() {
var tag = container.querySelector('button[data-index="0"]');
triggerEvent(tag, 'click');
assert.isTrue(tag.className.includes('icon-selected'));
});
test('choose custom tag', function() {
var tags = container.querySelectorAll('button');
triggerEvent(customTag, 'touchend');
for (var i = 0; i < tags.length; i++) {
assert.isFalse(tags[i].classList.contains('icon-selected'));
}
});
teardown(function() {
customTag.removeEventListener('touchend', subject.touchCustomTag);
});
});
});

View file

@ -0,0 +1,465 @@
'use strict';
/* global Contacts, contacts, Loader, ActivityHandler, LazyLoader,
MockContactsListObj, MockCookie, MockMozL10n,
MockNavigationStack, MockUtils, MocksHelper,
MockContactAllFields, MockContactDetails, Search,
MockContactsSearch, MockContactsSettings, Mockfb, MockLoader,
MockImportStatusData, MockMozContacts, ContactsService, HeaderUI
*/
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/test/unit/mock_l10n.js');
requireApp('communications/contacts/test/unit/mock_cache.js');
requireApp('communications/contacts/test/unit/mock_contacts_list_obj.js');
requireApp('communications/contacts/test/unit/mock_cookie.js');
requireApp('communications/contacts/test/unit/mock_datastore_migrator.js');
requireApp('communications/contacts/test/unit/mock_event_listeners.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
// requireApp('communications/contacts/test/unit/mock_main_navigation.js');
requireApp('communications/contacts/test/unit/mock_activities.js');
requireApp('communications/contacts/test/unit/mock_contacts_details.js');
requireApp('communications/contacts/test/unit/mock_contacts_search.js');
requireApp('communications/contacts/test/unit/mock_contacts_settings.js');
requireApp('communications/contacts/test/unit/mock_fb.js');
requireApp('communications/contacts/test/unit/mock_import_status_data.js');
requireApp('communications/contacts/test/unit/mock_loader.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_contact_all_fields.js');
var mocksForStatusBar = new MocksHelper([
'ActivityHandler',
'Cache',
'DatastoreMigration',
'LazyLoader'
]).init();
if (!window.navigationStack) {
window.navigationStack = null;
}
if (!window.contacts) {
window.contacts = null;
}
if (!window.utils) {
window.utils = null;
}
if (!navigator.mozContacts) {
navigator.mozContacts = null;
}
var globals = ['COMMS_APP_ORIGIN',
'TAG_OPTIONS',
'_',
'asyncScriptsLoaded',
'SCALE_RATIO',
'Contacts'];
globals.forEach(function(item) {
if (!window[item]) {
window[item] = null;
}
});
suite('Contacts', function() {
var realNavigationStack;
var realMozL10n;
var realContacts;
var realUtils;
var realFb;
var realImportStatusData;
var mockNavigation;
var realMozContacts;
var realLoader;
mocksForStatusBar.attachTestHelpers();
suiteSetup(function(done) {
loadBodyHTML('/contacts/index.html');
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockMozL10n;
sinon.spy(navigator.mozL10n, 'once');
realMozContacts = navigator.mozContacts;
navigator.mozContacts = MockMozContacts;
realContacts = window.contacts;
window.contacts = {};
window.contacts.List = MockContactsListObj;
window.contacts.Details = MockContactDetails;
window.Search = MockContactsSearch;
window.contacts.Settings = MockContactsSettings;
realUtils = window.utils;
window.utils = MockUtils;
window.utils.cookie = MockCookie;
realFb = window.fb;
window.fb = Mockfb;
realImportStatusData = window.ImportStatusData;
window.ImportStatusData = MockImportStatusData;
realNavigationStack = window.navigationStack;
window.navigationStack = MockNavigationStack;
realLoader = window.Loader;
window.Loader = MockLoader;
sinon.spy(window, 'navigationStack');
requireApp('communications/contacts/js/utilities/performance_helper.js');
requireApp('communications/contacts/js/main_navigation.js');
requireApp('communications/contacts/js/header_ui.js');
requireApp('communications/contacts/js/contacts.js', done);
});
suiteTeardown(function() {
navigator.mozL10n = realMozL10n;
navigator.mozContacts = realMozContacts;
window.contacts = realContacts;
window.utils = realUtils;
window.fb = realFb;
window.ImportStatusData = realImportStatusData;
window.Loader = realLoader;
window.navigationStack.restore();
window.navigationStack = realNavigationStack;
});
setup(function() {
this.sinon.spy(window.utils.PerformanceHelper, 'chromeInteractive');
this.sinon.spy(window.utils.PerformanceHelper, 'contentInteractive');
window.ImportStatusData.clear();
navigator.addIdleObserver = function() {};
// We don't want to trigger migrations in this test suite.
MockCookie.data = {
fbMigrated: true,
accessTokenMigrated: true
};
Contacts.init();
mockNavigation = window.navigationStack.firstCall.thisValue;
navigator.mozL10n.fireOnce();
});
test('hashchange home', function(done) {
this.sinon.spy(mockNavigation, 'home');
window.location.hash = '#home';
setTimeout(function() {
sinon.assert.called(mockNavigation.home);
done();
});
});
test('mozL10n initialized', function() {
sinon.assert.calledOnce(navigator.mozL10n.once);
sinon.assert.calledOnce(window.utils.PerformanceHelper.chromeInteractive);
sinon.assert.calledOnce(window.utils.PerformanceHelper.contentInteractive);
});
suite('on contacts change', function() {
var mozContact = null;
setup(function() {
mozContact = new MockContactAllFields();
Contacts.setCurrent(mozContact);
this.sinon.stub(
ContactsService,
'get',
function(id, cb) {
// Return the contact + additional FB info
cb(
mozContact,
{
id: 'FBID',
email: [
{
type: ['work'],
value: 'myfbemail@email.com'
}
]
}
);
}
);
this.sinon.stub(contacts.List, 'refresh', function(id, cb) {
cb();
});
});
test('> FB contact update sends MozContacts info', function() {
mockNavigation._currentView = 'view-contact-details';
navigator.mozContacts.dispatchEvent({
type: 'contactchange',
contactID: mozContact.id,
reason: 'update'
});
sinon.assert.called(ContactsService.get);
sinon.assert.called(contacts.List.refresh);
var argument = contacts.List.refresh.getCall(0).args[0];
assert.isTrue(Array.isArray(argument.email));
argument.email.forEach(function onEmail(email) {
assert.isTrue(email.value === 'myfbemail@email.com');
});
});
suite('> Custom contact change', function() {
test('> Trigger custom event on contact change', function(done) {
Contacts.onLocalized();
mockNavigation._currentView = 'view-contact-details';
document.addEventListener('contactChanged', function(e) {
assert.equal(e.detail.contactID, 1234567);
done();
});
navigator.mozContacts.dispatchEvent({
type: 'contactchange',
contactID: 1234567,
reason: 'update'
});
});
});
});
suite('Controller actions', function() {
var navigation;
setup(function() {
navigation = window.navigationStack.firstCall.thisValue;
this.sinon.spy(navigation, 'back');
this.sinon.spy(navigation, 'home');
this.sinon.spy(window.ActivityHandler, 'postCancel');
});
test('> go back', function() {
Contacts.goBack();
sinon.assert.called(navigation.back);
navigation.back.restore();
});
test('> handle cancel with activity', function() {
ActivityHandler.currentlyHandling = true;
Contacts.cancel();
sinon.assert.called(window.ActivityHandler.postCancel);
sinon.assert.called(navigation.home);
window.ActivityHandler.currentlyHandling = false;
});
test('> handle cancel without activity', function() {
Contacts.cancel();
sinon.assert.called(navigation.back);
sinon.assert.notCalled(window.ActivityHandler.postCancel);
});
suite('> CancelableActivity', function() {
var prevCurrentlyHandling, prevActivityName, prevActivityDataType;
suiteSetup(function() {
prevCurrentlyHandling = window.ActivityHandler.currentlyHandling;
prevActivityName = window.ActivityHandler.activityName;
prevActivityDataType = window.ActivityHandler.activityDataType;
window.ActivityHandler.currentlyHandling = true;
});
setup(function() {
});
teardown(function() {
window.ActivityHandler.activityName = prevActivityName;
window.ActivityHandler.activityDataType = prevActivityDataType;
});
suiteTeardown(function() {
window.ActivityHandler.currentlyHandling = prevCurrentlyHandling;
});
test('> handling an activity', function() {
ActivityHandler.isCancelable().then(() => {
HeaderUI.updateHeader(true);
// Settings is hidden
assert.isTrue(HeaderUI.settingsButton.hidden);
// Add contact is hidden
assert.isTrue(HeaderUI.addButton.hidden);
// Cancel is visible
assert.equal(HeaderUI.header.getAttribute('action'), 'close');
// Title shows CONTACTS
assert.equal(HeaderUI.appTitleElement.getAttribute('data-l10n-id'),
'contacts');
});
});
});
});
suite('Select a contact from the list', function() {
var theSelectedContact = {
'id': '1',
'name': ['John']
};
var navigation;
setup(function() {
navigation = window.navigationStack.firstCall.thisValue;
this.sinon.stub(Loader, 'view', function(view, cb) {
cb();
});
this.sinon.stub(ContactsService, 'get',
function(id, cb) {
cb(theSelectedContact, null);
});
this.sinon.spy(ActivityHandler, 'dataPickHandler');
this.sinon.spy(contacts.Details, 'render');
this.sinon.spy(navigation, 'go');
});
test('> initializing details', function() {
Contacts.showContactDetail('1');
sinon.assert.called(Loader.view);
sinon.assert.called(ContactsService.get);
sinon.assert.called(contacts.Details.render);
sinon.assert.calledWith(navigation.go,
'view-contact-details', 'go-deeper');
sinon.assert.notCalled(ActivityHandler.dataPickHandler);
});
test('> when handling pick activity, don\'t navigate, send result',
function() {
ActivityHandler.currentlyHandling = true;
ActivityHandler.activityName = 'pick';
Contacts.showContactDetail('1');
sinon.assert.called(ContactsService.get);
sinon.assert.notCalled(contacts.Details.render);
sinon.assert.notCalled(navigation.go);
sinon.assert.called(ActivityHandler.dataPickHandler);
ActivityHandler.currentlyHandling = false;
ActivityHandler.activityName = 'open';
}
);
test('> when handling import activity, navigate as normal',
function() {
ActivityHandler.currentlyHandling = true;
ActivityHandler.activityName = 'import';
Contacts.showContactDetail('1');
sinon.assert.called(ContactsService.get);
sinon.assert.called(contacts.Details.render);
sinon.assert.called(navigation.go);
sinon.assert.notCalled(ActivityHandler.dataPickHandler);
ActivityHandler.currentlyHandling = false;
ActivityHandler.activityName = 'open';
}
);
test('> in search navigate deeper from search', function() {
sinon.stub(Search, 'isInSearchMode', function() {
return true;
});
Contacts.showContactDetail('1');
sinon.assert.called(contacts.Details.render);
sinon.assert.called(Search.isInSearchMode);
sinon.assert.calledWith(navigation.go,
'view-contact-details', 'go-deeper-search');
sinon.assert.notCalled(ActivityHandler.dataPickHandler);
Search.isInSearchMode.restore();
});
});
suite('Async scripts loading', function() {
var lastParams;
var handler;
setup(function() {
this.sinon.stub(ContactsService, 'addListener', function(evt, cb) {
handler = cb;
});
this.sinon.stub(LazyLoader, 'load', function(p, cb) {
lastParams = p;
cb();
});
});
teardown(function() {
handler = null;
});
test('> normal load of the scripts', function() {
Contacts.onLocalized().then(() => {
sinon.assert.called(window.dispatchEvent);
assert.isNotNull(handler);
});
});
test('> loading scripts while handling an open activity',
function() {
ActivityHandler.currentlyHandling = true;
Contacts.onLocalized().then(() => {
assert.isNotNull(handler);
ActivityHandler.currentlyHandling = false;
});
});
});
suite('Visibility changes', function() {
var navigation;
function fireVisibilityChange() {
document.dispatchEvent(new CustomEvent('visibilitychange'));
}
setup(function() {
navigation = window.navigationStack.firstCall.thisValue;
this.sinon.spy(ActivityHandler, 'isCancelable');
this.sinon.spy(ActivityHandler, 'postCancel');
this.sinon.stub(Loader, 'view', function(view, cb) {
cb();
});
});
test('> in settings view, should refresh for new timestamp', function() {
sinon.stub(navigation, 'currentView', function() {
return 'view-settings';
});
sinon.spy(contacts.Settings, 'updateTimestamps');
fireVisibilityChange();
sinon.assert.called(contacts.Settings.updateTimestamps);
sinon.assert.notCalled(ActivityHandler.postCancel);
navigation.currentView.restore();
contacts.Settings.updateTimestamps.restore();
});
suite('> going to the background', function() {
var realHidden;
suiteSetup(function() {
realHidden = Object.getOwnPropertyDescriptor(document, 'hidden');
Object.defineProperty(document, 'hidden', {
configurable: true,
get: function() { return true; }
});
});
suiteTeardown(function() {
if (realHidden) {
Object.defineProperty(document, 'hidden', realHidden);
} else {
delete document.hidden;
}
});
});
});
});

View file

@ -0,0 +1,292 @@
'use strict';
/* global DatastoreMigration */
/* global MockContactsListObj */
/* global MockCookie */
/* global MockFbContactsWriterObj */
/* global MockIndexedDB */
requireApp('communications/contacts/test/unit/mock_fb_data.js');
requireApp('communications/contacts/test/unit/mock_contacts_list_obj.js');
require('/shared/test/unit/mocks/mock_indexedDB.js');
requireApp('communications/contacts/test/unit/mock_cookie.js');
requireApp('communications/contacts/js/fb/datastore_migrator.js');
var realFbData, realContacts,
realRemoveIdleObserver, realCookie, mockIndexedDB;
if (!window.realFbData) {
window.realFbData = null;
}
if (!window.fb) {
window.fb = null;
}
if (!window.contacts) {
window.contacts = null;
}
if (!window.realContacts) {
window.realContacts = null;
}
if (!window.realRemoveIdleObserver) {
window.realRemoveIdleObserver = null;
}
if (!window.realCookie) {
window.realCookie = null;
}
if (!window.utils) {
window.utils = null;
}
if (!window.mockIndexedDB) {
window.mockIndexedDB = null;
}
suite('IndexedDB --> Datastore migration', function() {
var idbShortData = {
'123456': {
uid: '123456'
},
'567890': {
uid: '567890'
}
};
var idbLongData = {
'123456': {
uid: '123456'
},
'567890': {
uid: '567890'
},
'987654': {
uid: '987654'
},
'387643': {
uid: '387643'
},
'999999': {
uid: '999999'
},
'111111': {
uid: '111111'
}
};
suiteSetup(function() {
realFbData = window.fb && window.fb.contacts;
window.fb = window.fb || {};
realContacts = window.contacts && window.contacts.List;
window.contacts = window.contacts || {};
window.contacts.List = MockContactsListObj;
realRemoveIdleObserver = navigator.removeIdleObserver;
navigator.removeIdleObserver = function() { };
realCookie = window.utils && window.utils.cookie;
window.utils = window.utils || {};
window.utils.cookie = MockCookie;
mockIndexedDB = new MockIndexedDB();
});
function testMigration(records, done) {
var existingRecords = records.existing;
var inputRecords = records.input;
var inErrorRecords = records.inError;
var db;
var contactsWriter = new MockFbContactsWriterObj();
window.fb.contacts = contactsWriter;
contactsWriter.storedData = existingRecords || contactsWriter.storedData;
MockCookie.data = {};
var numDeletedDBs = mockIndexedDB.deletedDbs.length;
var deleteError = [];
if (inErrorRecords && inErrorRecords.deleted) {
deleteError = inErrorRecords.deleted;
}
var saveError = [];
if (inErrorRecords && inErrorRecords.saved) {
saveError = inErrorRecords.saved;
contactsWriter.savedError = saveError;
}
var migrationDone = function migrationDone(saveErrors) {
var deletedObjs = db.deletedData;
var numAddedObjs = contactsWriter.getLength();
assert.equal(Object.keys(inputRecords).length,
numAddedObjs + saveError.length);
assert.equal(deletedObjs.length + deleteError.length, numAddedObjs);
for (var aObjUid in contactsWriter.storedData) {
assert.isTrue(deletedObjs.indexOf(aObjUid) !== -1 ||
deleteError.indexOf(aObjUid) !== -1);
}
assert.isTrue(db.isClosed === true);
if (!saveErrors) {
// Check that cookies are correctly set
assert.equal(MockCookie.data.fbMigrated, true);
assert.isTrue(mockIndexedDB.deletedDbs.length === numDeletedDBs + 1);
}
else {
assert.isTrue(mockIndexedDB.deletedDbs.length === numDeletedDBs);
}
done();
};
var req = window.indexedDB.open('Fake_Database');
req.onsuccess = function() {
db = req.result;
db.storedData = JSON.parse(JSON.stringify(inputRecords));
var migrationObj = new DatastoreMigration(db);
migrationObj.onmigrationdone = migrationDone;
migrationObj.onerror = done;
if (inErrorRecords && inErrorRecords.deleted) {
db.options.deleteInError = inErrorRecords.deleted;
}
migrationObj.onidle();
};
req.onerror = done;
}
test('Seamless migration. Total records < slice size. No failures',
function(done) {
testMigration({ input: idbShortData }, done);
});
test('Seamless migration. Total records > slice size. No failures',
function(done) {
testMigration({ input: idbLongData }, done);
});
test('If indexedDB does not contain records, nothing is done',
function(done) {
testMigration({ input: {} }, done);
});
test('If indexedDB is in error, an error is reported', function(done) {
var contactsWriter = new MockFbContactsWriterObj();
window.fb.contacts = contactsWriter;
MockCookie.data = {};
mockIndexedDB.options.inErrorDbs = ['Gaia_Facebook_Friends'];
var migrationObj = new DatastoreMigration();
migrationObj.onerror = function() {
assert.equal(contactsWriter.getLength(), 0);
assert.isFalse((MockCookie.data.fbMigrated === true));
done();
};
migrationObj.onmigrationdone = function() {
assert.fail('Migration done');
done();
};
migrationObj.onidle();
});
test('If indexedDB does not exist, nothing is done', function(done) {
var contactsWriter = new MockFbContactsWriterObj();
window.fb.contacts = contactsWriter;
MockCookie.data = {};
mockIndexedDB.options.upgradeNeededDbs = ['Gaia_Facebook_Friends'];
mockIndexedDB.options.inErrorDbs = [];
var migrationObj = new DatastoreMigration();
migrationObj.onmigrationdone = function() {
assert.equal(contactsWriter.getLength(), 0);
assert.equal(MockCookie.data.fbMigrated, true);
done();
};
migrationObj.onidle();
});
test('If a migrated record already exists the migration continues seamlessly',
function(done) {
var existing = Object.create(null);
existing['123456'] = {
uid: '123456'
};
testMigration({
input: idbLongData,
existing: existing
}, done);
});
test('If a record cannot be deleted the migration continues seamlessly',
function(done) {
var inError = {
deleted: ['987654']
};
testMigration({
input: idbLongData,
inError: inError
}, done);
});
test('If cursor cannot be opened the process can resume later',
function(done) {
var contactsWriter = new MockFbContactsWriterObj();
window.fb.contacts = contactsWriter;
var db;
var req = window.indexedDB.open('Fake_Database');
req.onsuccess = function() {
db = req.result;
db.options.cursorOpenInError = true;
var migratorObj = new DatastoreMigration(db);
db.storedData = JSON.parse(JSON.stringify(idbShortData));
migratorObj.onerror = function() {
assert.ok('Migration error');
migratorObj.onmigrationdone = function() {
assert.ok('Migration done');
done();
};
delete db.options.cursorOpenInError;
// Need to restore the Database before calling onidle one more time
migratorObj.db = db;
migratorObj.onidle();
};
migratorObj.onmigrationdone = function() {
assert.fail('Migration done', 'Shouldnt');
};
migratorObj.onidle();
};
req.onerror = done;
});
test(
'If a record cannot be saved the process can continue without losing data',
function(done) {
var inError = {
saved: ['387643']
};
testMigration({
input: idbLongData,
inError: inError
}, done);
});
suiteTeardown(function() {
window.fb.contacts = realFbData;
window.contacts.List = realContacts;
window.utils.cookie = realCookie;
navigator.removeIdleObserver = realRemoveIdleObserver;
});
});

View file

@ -0,0 +1,225 @@
'use strict';
/* global MockImportStatusData, Mockfb, MockContacts, MockNavigationStack,
MockCookie, DeferredActions, fb */
require('/shared/js/lazy_loader.js');
requireApp('communications/contacts/test/unit/mock_import_status_data.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
requireApp('communications/contacts/test/unit/mock_contacts_list_obj.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
requireApp('communications/contacts/test/unit/mock_cookie.js');
requireApp('communications/contacts/test/unit/mock_fb.js');
requireApp('communications/contacts/js/deferred_actions.js');
if (!navigator.addIdleObserver) {
navigator.addIdleObserver = function() {};
}
if (!navigator.removeIdleObserver) {
navigator.removeIdleObserver = function() {};
}
if (!window.ImportStatusData) {
window.ImportStatusData = null;
}
if (!window.contacts) {
window.contacts = null;
}
if (!window.utils) {
window.utils = null;
}
if (!window.fb) {
window.fb = null;
}
suite('Post rendering', function() {
var realImportStatusData,
realUtils,
realFb,
realFbLoader;
var mockNavigationStack;
suiteSetup(function() {
realImportStatusData = window.ImportStatusData;
window.ImportStatusData = MockImportStatusData;
window.Contacts = MockContacts;
realUtils = window.utils;
window.utils = {};
window.utils.cookie = MockCookie;
realFb = window.fb;
window.fb = Mockfb;
mockNavigationStack = new MockNavigationStack();
});
suiteTeardown(function() {
window.ImportStatusData = realImportStatusData;
});
suite('Post rendering actions', function() {
suiteSetup(function() {
sinon.stub(navigator, 'addIdleObserver', function(idleObserver) {
idleObserver.onidle();
});
sinon.stub(navigator, 'removeIdleObserver', function() {});
realFbLoader = window.fbLoader;
window.fbLoader = {
loaded: true
};
});
suiteTeardown(function() {
navigator.addIdleObserver.restore();
navigator.removeIdleObserver.restore();
window.fbLoader = realFbLoader;
});
setup(function() {
MockImportStatusData.clear();
MockCookie.update({});
});
test('FB sync scheduling when synced in ftu', function(done) {
sinon.stub(Mockfb.sync, 'scheduleNextSync', function() {
done(function() {
Mockfb.sync.scheduleNextSync.restore();
assert.ok('passed');
});
});
MockCookie.update({
fbMigrated: true,
accessTokenMigrated: true
});
window.ImportStatusData.put(
Mockfb.utils.SCHEDULE_SYNC_KEY, Date.now()).then(function() {
DeferredActions.execute();
});
});
test('Facebook not yet loaded', function(done) {
var currentFbLoader = window.fbLoader;
window.fbLoader = {
loaded: false
};
sinon.stub(Mockfb.sync, 'scheduleNextSync', function() {
done(function() {
Mockfb.sync.scheduleNextSync.restore();
window.fbLoader = currentFbLoader;
assert.ok('passed');
});
});
MockCookie.update({
fbMigrated: true,
accessTokenMigrated: true
});
window.ImportStatusData.put(
Mockfb.utils.SCHEDULE_SYNC_KEY, Date.now()).then(function() {
DeferredActions.execute();
window.dispatchEvent(new CustomEvent('facebookLoaded'));
});
});
test('Version migration triggered when needed', function(done) {
sinon.stub(window.LazyLoader, 'load', function(file) {
if (file.indexOf('migrator.js') > -1) {
done(function() {
window.LazyLoader.load.restore();
assert.ok('passed');
});
}
});
MockCookie.update({
fbScheduleDone: true
});
DeferredActions.execute();
});
suite('Facebook sanity checks', function() {
var spy;
suiteSetup(function() {
fb.utils = {
clearFbData: function() {
return {
result: {
set onsuccess(cb2) {
cb2();
}
},
set onsuccess(cb) {
cb();
}
};
}
};
spy = sinon.spy(fb.utils, 'clearFbData');
});
suiteTeardown(function() {
// Left things as they were
spy.restore();
delete fb.utils;
});
test('Not called when not needed', function() {
spy.reset();
MockCookie.update({
fbCleaningInProgress: 0
});
DeferredActions.execute();
assert.isFalse(spy.calledOnce);
assert.equal(MockCookie.data.fbCleaningInProgress, 0);
});
test('Clear Facebook data called when needed', function() {
MockCookie.update({
fbCleaningInProgress: 1
});
DeferredActions.execute();
assert.isTrue(spy.calledOnce);
assert.equal(MockCookie.data.fbCleaningInProgress, 0);
});
test('Not called when num retries exceeded', function() {
var times = 5;
spy.reset();
MockCookie.update({
fbCleaningInProgress: times
});
DeferredActions.execute();
assert.isFalse(spy.calledOnce);
assert.equal(MockCookie.data.fbCleaningInProgress, times);
});
});
});
});

View file

@ -0,0 +1,245 @@
'use strict';
/* globals ContactsBTExport, MockContactToVcarBlob, MockgetDeviceStorage,
MockGetStorageIfAvailable, MockGetStorageIfAvailableError,
MockGetUnusedFilename, MocksHelper, MockMozActivity, MockMozL10n */
requireApp('communications/contacts/js/export/bt.js');
requireApp('communications/contacts/test/unit/mock_mozActivity.js');
requireApp('communications/contacts/test/unit/mock_get_device_storage.js');
requireApp('communications/contacts/test/unit/export/mock_export_utils.js');
requireApp('communications/contacts/test/unit/mock_l10n.js');
if (!window._) {
window._ = null;
}
if (!window.getStorageIfAvailable) {
window.getStorageIfAvailable = null;
}
if (!window.getUnusedFilename) {
window.getUnusedFilename = null;
}
if (!window.ContactToVcardBlob) {
window.ContactToVcardBlob = null;
}
var realUtils = null;
if (!window.utils) {
window.utils = null;
} else {
realUtils = window.utils;
}
var mocksHelperForExportBT = new MocksHelper([
'MozActivity'
]).init();
suite('BT export', function() {
var real_,
realMozL10n,
realDeviceStorage,
realgetUnusedFilename,
realContactToVcardBlob,
realgetStorageIfAvailable;
var mockContact1 = {
familyName: ['surname1'],
givenName: ['name1']
},
mockContact2 = {
familyName: ['surname2'],
givenName: ['name2']
};
var subject,
mockProgress,
mocksHelper = mocksHelperForExportBT;
suiteSetup(function() {
mocksHelper.suiteSetup();
// Device storage mock
realDeviceStorage = navigator.getDeviceStorage;
navigator.getDeviceStorage = MockgetDeviceStorage;
// L10n mock
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockMozL10n;
real_ = window._;
window._ = navigator.mozL10n.get;
//getStorageIfAvailable mock
realgetStorageIfAvailable = window.getStorageIfAvailable;
window.getStorageIfAvailable = MockGetStorageIfAvailable;
//getUnusedFilename mock
realgetUnusedFilename = window.getUnusedFilename;
window.getUnusedFilename = MockGetUnusedFilename;
//ContactToVcardBlob mock
realContactToVcardBlob = window.ContactToVcardBlob;
window.ContactToVcardBlob = MockContactToVcarBlob;
if (!window.utils) {
window.utils = {};
}
window.utils.overlay = {
hideMenu: function() {}
};
mockProgress = function() {};
});
suiteTeardown(function() {
mocksHelper.suiteTeardown();
navigator.getDeviceStorage = realDeviceStorage;
window._ = real_;
window.getStorageIfAvailable = realgetStorageIfAvailable;
window.getUnusedFilename = realgetUnusedFilename;
window.ContactToVcardBlob = realContactToVcardBlob;
window.utils = realUtils;
});
setup(function() {
mocksHelper.setup();
subject = new ContactsBTExport();
subject.setProgressStep(mockProgress);
});
teardown(function() {
mocksHelper.teardown();
});
test('Calling with 1 contact', function(done) {
subject.setContactsToExport([mockContact1]);
subject.doExport(function onFinish(error, exported, msg) {
assert.equal(false, subject.hasDeterminativeProgress());
assert.isNull(error);
assert.equal(1, exported);
done();
});
// Execute the onsuccess on demand
MockMozActivity.currentActivity.onsuccess();
});
test('Calling with several contacts', function(done) {
var contacts = [mockContact1, mockContact2];
subject.setContactsToExport(contacts);
subject.doExport(function onFinish(error, exported, msg) {
assert.isNull(error);
assert.equal(contacts.length, exported);
done();
});
// Execute the onsuccess on demand
MockMozActivity.currentActivity.onsuccess();
});
test('Calling with cancel flag activated', function(done) {
var contacts = [mockContact1, mockContact2];
subject.setContactsToExport(contacts);
subject.cancelExport();
subject.doExport(function onFinish(error, exported, isRecoverable) {
assert.isNull(error);
assert.equal(0, exported);
assert.equal(isRecoverable, false);
done();
});
});
});
suite('BT export_error', function() {
var real_,
realMozL10n,
realDeviceStorage,
realgetUnusedFilename,
realContactToVcardBlob,
realgetStorageIfAvailable;
var mockContact1 = {
familyName: ['surname1'],
givenName: ['name1']
},
mockContact2 = {
familyName: ['surname2'],
givenName: ['name2']
};
var subject,
mockProgress,
mocksHelper = mocksHelperForExportBT;
suiteSetup(function() {
mocksHelper.suiteSetup();
// Device storage mock
realDeviceStorage = navigator.getDeviceStorage;
navigator.getDeviceStorage = MockgetDeviceStorage;
// L10n mock
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockMozL10n;
real_ = window._;
window._ = navigator.mozL10n.get;
//getStorageIfAvailable mock
realgetStorageIfAvailable = window.getStorageIfAvailable;
window.getStorageIfAvailable = MockGetStorageIfAvailableError;
//getUnusedFilename mock
realgetUnusedFilename = window.getUnusedFilename;
window.getUnusedFilename = MockGetUnusedFilename;
//ContactToVcardBlob mock
realContactToVcardBlob = window.ContactToVcardBlob;
window.ContactToVcardBlob = MockContactToVcarBlob;
if (!window.utils) {
window.utils = {};
}
window.utils.overlay = {
hideMenu: function() {}
};
mockProgress = function() {};
});
suiteTeardown(function() {
mocksHelper.suiteTeardown();
navigator.getDeviceStorage = realDeviceStorage;
window._ = real_;
window.getStorageIfAvailable = realgetStorageIfAvailable;
window.getUnusedFilename = realgetUnusedFilename;
window.ContactToVcardBlob = realContactToVcardBlob;
window.utils = realUtils;
});
setup(function() {
mocksHelper.setup();
subject = new ContactsBTExport();
subject.setProgressStep(mockProgress);
});
teardown(function() {
mocksHelper.teardown();
});
test('Calling with error', function(done) {
var contacts = [mockContact1, mockContact2];
subject.setContactsToExport(contacts);
subject.doExport(function onFinish(error, exported, isRecoverable) {
assert.isNotNull(error);
assert.equal(error.reason, 'noSpace');
assert.equal(0, exported);
assert.equal(isRecoverable, false);
done();
});
});
});

View file

@ -0,0 +1,271 @@
'use strict';
/* global ContactsService */
/* global MockContactsList */
/* global MockConfirmDialog */
/* global ContactsExporter */
/* global MockExportStrategy */
/* global MockMozContacts */
/* global MockMozL10n */
/* global MocksHelper */
/* global MockLoader */
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
requireApp('communications/contacts/test/unit/mock_export_strategy.js');
requireApp('communications/contacts/test/unit/mock_contacts_list.js');
requireApp('communications/contacts/test/unit/mock_l10n.js');
requireApp('communications/contacts/test/unit/mock_loader.js');
require('/shared/test/unit/mocks/mock_confirm_dialog.js');
require('/shared/js/fb/fb_reader_utils.js');
requireApp('communications/contacts/js/export/contacts_exporter.js');
var realUtils = null;
var realStatus = null;
var realLoader = null;
var real_ = null;
if (!window.utils) {
window.utils = null;
} else {
realUtils = window.utils;
}
if (!window.status) {
window.status = null;
} else {
realStatus = window.status;
}
if (!navigator.mozL10n) {
navigator.mozL10n = null;
}
if (!window._) {
window._ = null;
}
var mocksHelperForExporter = new MocksHelper([
'Contacts',
'ConfirmDialog',
'Loader'
]).init();
suite('Contacts Exporter', function() {
var subject;
var ids = ['1', '3'];
var realL10n;
var menuOverlay;
function getContactsForIds(ids) {
var contacts = MockContactsList();
var result = [];
ids.forEach(function onId(id) {
for (var i = 0; i < contacts.length; i++) {
if (contacts[i].id == id) {
result.push(contacts[i]);
return;
}
}
});
return result;
}
suiteSetup(function() {
realL10n = navigator.mozL10n;
navigator.mozL10n = MockMozL10n;
real_ = window._;
window._ = navigator.mozL10n.get;
realLoader = window.Loader;
window.Loader = MockLoader;
navigator.mozContacts = MockMozContacts;
sinon.spy(MockExportStrategy, 'shouldShowProgress');
sinon.spy(MockExportStrategy, 'doExport');
sinon.spy(MockExportStrategy, 'hasDeterminativeProgress');
sinon.spy(MockExportStrategy, 'setProgressStep');
if (!window.utils) {
window.utils = {};
}
window.utils.time = {
pretty: function(date) {
return date;
}
};
window.utils.overlay = {
show: function() {
return {
setTotal: function(l) {}
};
},
hide: function() {},
showMenu: function() {}
};
window.utils.status = {
show: function(msg) {},
hide: function() {}
};
sinon.spy(window.utils.status, 'show');
mocksHelperForExporter.suiteSetup();
});
suiteTeardown(function() {
window.navigator.mozL10n = realL10n;
window.Loader = realLoader;
MockExportStrategy.shouldShowProgress.restore();
MockExportStrategy.doExport.restore();
MockExportStrategy.hasDeterminativeProgress.restore();
MockExportStrategy.setProgressStep.restore();
mocksHelperForExporter.suiteTeardown();
});
setup(function() {
this.sinon.stub(ContactsService, 'getAll', function(cb) {
var contacts = MockContactsList();
setTimeout(function() {
cb(null, contacts);
});
});
menuOverlay = document.createElement('form');
menuOverlay.innerHTML = '<menu>' +
'<button data-l10n-id="cancel" id="cancel-overlay">Cancel</button>' +
'</menu>';
document.body.appendChild(menuOverlay);
subject = new ContactsExporter(MockExportStrategy);
MockExportStrategy.shouldShowProgress.reset();
MockExportStrategy.doExport.reset();
MockExportStrategy.hasDeterminativeProgress.reset();
MockExportStrategy.setProgressStep.reset();
});
teardown(function() {
menuOverlay.parentNode.removeChild(menuOverlay);
});
test('Correct initialization given an array of ids', function(done) {
subject.init(ids, function onInitDone(contacts) {
var expectedContacts = getContactsForIds(ids);
assert.lengthOf(contacts, 2);
assert.notStrictEqual(expectedContacts, contacts);
done();
});
});
suite('Strategy with deterministic progress >', function() {
setup(function(done) {
subject.init(ids, function onInitDone(contacts) {
subject.start();
done();
});
});
test('Progress bar is shown', function() {
assert.ok(MockExportStrategy.shouldShowProgress.calledOnce);
});
test('Export is attempted', function() {
assert.ok(MockExportStrategy.doExport.calledOnce);
});
test('The progress type is checked', function() {
assert.ok(MockExportStrategy.hasDeterminativeProgress.calledOnce);
});
test('Progress bar updates value', function() {
assert.ok(MockExportStrategy.setProgressStep.calledOnce);
});
test('Status shown', function() {
assert.isTrue(window.utils.status.show.called);
});
});
suite('Strategy with no deterministic progress >', function() {
setup(function(done) {
subject.init(ids, function onInitDone(contacts) {
MockExportStrategy.determinativeValue = false;
subject.start();
done();
});
});
teardown(function() {
MockExportStrategy.determinativeValue = true;
});
test('Progress bar is shown', function() {
assert.ok(MockExportStrategy.shouldShowProgress.calledOnce);
});
test('Export is attempted', function() {
assert.ok(MockExportStrategy.doExport.calledOnce);
});
test('The progress type is checked', function() {
assert.ok(MockExportStrategy.hasDeterminativeProgress.calledOnce);
});
test('Progress bar never updated', function() {
assert.ok(MockExportStrategy.setProgressStep.callCount === 0);
});
test('Status shown', function() {
assert.isTrue(window.utils.status.show.called);
});
});
suite('Error handling >', function() {
var error,
errorName;
setup(function(done) {
sinon.spy(MockConfirmDialog, 'show');
subject.init(ids, function onInitDone(contacts) {
error = {
'reason': 'BIGerror'
};
errorName = 'exportError-' +
MockExportStrategy.name + '-' +
error.reason;
MockExportStrategy.setError(error);
subject.start();
done();
});
});
teardown(function() {
MockConfirmDialog.show.restore();
});
test('Error dialog is called', function() {
assert.ok(MockConfirmDialog.show.calledOnce);
});
test('The proper error shows', function() {
// errors are called with the structure
// {title, error, retry, cancel}
assert.equal(MockConfirmDialog.show.args[0][1], errorName);
});
test('Status shown', function() {
assert.isTrue(window.utils.status.show.called);
});
});
});

View file

@ -0,0 +1,24 @@
/* exported MockContactToVcarBlob, MockGetStorageIfAvailable,
MockGetUnusedFilename, MockGetStorageIfAvailableError */
'use strict';
var MockGetStorageIfAvailable = function(type, size, callback) {
callback(navigator.getDeviceStorage());
};
var MockGetStorageIfAvailableError = function(type, size, callback, cbErr) {
if (cbErr) {
cbErr(0); // Simulate no space error
} else {
callback(navigator.getDeviceStorage());
}
};
var MockGetUnusedFilename = function(storage, filename, callback) {
callback(filename);
};
var MockContactToVcarBlob = function(contacts, callback) {
callback({ size: contacts.length });
};

View file

@ -0,0 +1,223 @@
/* globals ContactsSDExport, MockgetDeviceStorage, MockGetStorageIfAvailable,
MockGetStorageIfAvailableError, MockGetUnusedFilename */
'use strict';
requireApp('communications/contacts/js/export/sd.js');
requireApp('communications/contacts/test/unit/mock_get_device_storage.js');
requireApp('communications/contacts/test/unit/export/mock_export_utils.js');
suite('Sd export', function() {
var subject;
var realDeviceStorage = null;
var c1 = {
familyName: ['foo'],
givenName: ['bar']
};
var c2 = { givenName: ['bar'] };
var c3 = { familyName: ['foo'] };
var c4 = {};
var updateSpy = null;
var progressMock = function dummy() {};
var realContactToVcard = null;
var real_ = null;
var realgetStorageIfAvailable = null;
var realgetUnusedFilename = null;
var menuOverlay = null;
suiteSetup(function() {
// Device storage mock
realDeviceStorage = navigator.getDeviceStorage;
navigator.getDeviceStorage = MockgetDeviceStorage;
// L10n mock
real_ = window._;
window._ = function() {};
// getStorageIfAvailable mock
realgetStorageIfAvailable = window.getStorageIfAvailable;
window.getStorageIfAvailable = MockGetStorageIfAvailable;
// getUnusedFilename mock
realgetUnusedFilename = window.getUnusedFilename;
window.getUnusedFilename = MockGetUnusedFilename;
});
suiteTeardown(function() {
navigator.getDeviceStorage = realDeviceStorage;
window._ = real_;
window.getStorageIfAvailable = realgetStorageIfAvailable;
window.getUnusedFilename = realgetUnusedFilename;
});
setup(function() {
menuOverlay = document.createElement('form');
menuOverlay.innerHTML = '<menu>' +
'<button data-l10n-id="cancel" id="cancel-overlay">Cancel</button>' +
'</menu>';
document.body.appendChild(menuOverlay);
subject = new ContactsSDExport();
subject.setProgressStep(progressMock);
realContactToVcard = window.ContactToVcard;
window.ContactToVcard = function() {};
updateSpy = this.sinon.stub(
window,
'ContactToVcard',
function(contacts, append, finish) {
finish();
append(' ', contacts.length);
}
);
});
teardown(function() {
window.ContactToVcard = realContactToVcard;
menuOverlay.parentNode.removeChild(menuOverlay);
});
test('Calling with 1 contact', function(done) {
subject.setContactsToExport([c1]);
subject.doExport(function onFinish(error, exported, msg) {
done(function() {
assert.equal(false, subject.hasDeterminativeProgress());
assert.equal(1, updateSpy.callCount);
assert.isNull(error);
assert.equal(1, exported);
});
});
});
test('Calling with 1 contact with no given name', function(done) {
subject.setContactsToExport([c2]);
subject.doExport(function onFinish(error, exported, msg) {
done(function() {
assert.equal(false, subject.hasDeterminativeProgress());
assert.equal(1, updateSpy.callCount);
assert.isNull(error);
assert.equal(1, exported);
});
});
});
test('Calling with 1 contact with no family name', function(done) {
subject.setContactsToExport([c3]);
subject.doExport(function onFinish(error, exported, msg) {
done(function() {
assert.equal(false, subject.hasDeterminativeProgress());
assert.equal(1, updateSpy.callCount);
assert.isNull(error);
assert.equal(1, exported);
});
});
});
test('Calling with several contacts', function(done) {
var contacts = [c1, c2, c3, c4];
subject.setContactsToExport(contacts);
subject.doExport(function onFinish(error, exported, msg) {
done(function() {
assert.equal(1, updateSpy.callCount);
assert.isNull(error);
assert.equal(contacts.length, exported);
});
});
});
test('Calling with cancel flag activated', function(done) {
subject.setContactsToExport([c1]);
subject.cancelExport();
subject.doExport(function onFinish(error, exported, msg) {
done(function() {
assert.equal(1, updateSpy.callCount);
assert.isNull(error);
assert.equal(exported, 0);
});
});
});
});
suite('Sd export error', function() {
var subject;
var realDeviceStorage = null;
var c1 = {
familyName: ['foo'],
givenName: ['bar']
};
var updateSpy = null;
var progressMock = function dummy() {};
var realContactToVcard = null;
var real_ = null;
var realgetStorageIfAvailable = null;
var realgetUnusedFilename = null;
var menuOverlay = null;
suiteSetup(function() {
// Device storage mock
realDeviceStorage = navigator.getDeviceStorage;
navigator.getDeviceStorage = MockgetDeviceStorage;
// L10n mock
real_ = window._;
window._ = function() {};
// getStorageIfAvailable mock
realgetStorageIfAvailable = window.getStorageIfAvailable;
window.getStorageIfAvailable = MockGetStorageIfAvailableError;
// getUnusedFilename mock
realgetUnusedFilename = window.getUnusedFilename;
window.getUnusedFilename = MockGetUnusedFilename;
});
suiteTeardown(function() {
navigator.getDeviceStorage = realDeviceStorage;
window._ = real_;
window.getStorageIfAvailable = realgetStorageIfAvailable;
window.getUnusedFilename = realgetUnusedFilename;
});
setup(function() {
menuOverlay = document.createElement('form');
menuOverlay.innerHTML = '<menu>' +
'<button data-l10n-id="cancel" id="cancel-overlay">Cancel</button>' +
'</menu>';
document.body.appendChild(menuOverlay);
subject = new ContactsSDExport();
subject.setProgressStep(progressMock);
realContactToVcard = window.ContactToVcard;
window.ContactToVcard = function() {};
updateSpy = this.sinon.stub(
window,
'ContactToVcard',
function(contacts, append, finish) {
finish();
append(' ', contacts.length);
}
);
});
teardown(function() {
window.ContactToVcard = realContactToVcard;
menuOverlay.parentNode.removeChild(menuOverlay);
});
test('Calling with error', function(done) {
subject.setContactsToExport([c1]);
subject.doExport(function onFinish(error, exported, isRecoverable) {
assert.isNotNull(error);
assert.equal(error.reason, 'noSpace');
assert.equal(0, exported);
assert.equal(isRecoverable, true);
done();
});
});
});

View file

@ -0,0 +1,224 @@
'use strict';
/* global ContactsSIMExport */
/* global MockMozContacts */
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/js/export/sim.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
suite('Sim export', function() {
var realMozContacts = null;
var c1 = {}, c2 = {};
var progressMock = function dummy() {};
var iccContactId = '1234567';
var iccid = '999999999';
function getIccContactUrl(iccid, iccContactId) {
return 'urn:uuid:' + iccid + '-' + iccContactId;
}
function setupManagerAndSubject(toExport) {
var iccManager = new IccManager();
var updateSpy = sinon.spy(iccManager, 'updateContact');
var subject = new ContactsSIMExport(iccManager);
subject.setProgressStep(progressMock);
subject.setContactsToExport(toExport);
return {
subject: subject,
manager: iccManager,
updateSpy: updateSpy
};
}
function IccManager() {
this.calledCount = 0,
this.faulty = false,
this.simFull = false,
this.updateContact = function(type, contact) {
var self = this;
return {
result: {
id: iccContactId
},
set onsuccess(cb) {
self.calledCount++;
if ((!self.faulty && !self.simFull) || self.calledCount % 2 !== 0) {
window.setTimeout(cb);
}
},
set onerror(cb) {
if (self.faulty && self.calledCount % 2 === 0) {
this.error = {
name: 'ContactTypeNotSupported'
};
window.setTimeout(function() {
cb({
target: this
});
}.bind(this));
return;
}
if (self.simFull && self.calledCount % 2 === 0) {
this.error = {
name: 'NoFreeRecordFound'
};
window.setTimeout(function() {
cb({
target: this
});
}.bind(this));
}
}
};
};
this.iccInfo = {
iccid: iccid
};
}
var iccContactIdUrl = {
type: ['source', 'sim'],
value: getIccContactUrl(iccid, iccContactId)
};
suiteSetup(function() {
realMozContacts = navigator.mozContacts;
navigator.mozContacts = MockMozContacts;
});
suiteTeardown(function() {
navigator.mozContacts = realMozContacts;
});
test('Calling with 1 contact', function(done) {
var obj = setupManagerAndSubject([c1]);
obj.subject.doExport(function onFinish(error, exported) {
assert.equal(false, obj.subject.hasDeterminativeProgress());
assert.ok(obj.updateSpy.calledOnce);
assert.isNull(error);
assert.equal(1, exported);
done();
});
});
test('Calling with several contacts', function(done) {
var contacts = [c1, c2];
var obj = setupManagerAndSubject(contacts);
obj.subject.doExport(function onFinish(error, exported) {
assert.ok(obj.subject.hasDeterminativeProgress());
assert.equal(contacts.length, obj.updateSpy.callCount);
assert.isNull(error);
assert.equal(contacts.length, exported);
done();
});
});
test('If SIM Card is full the error is reported properly', function(done) {
var contacts = [c1, c2];
var obj = setupManagerAndSubject(contacts);
obj.manager.simFull = true;
obj.subject.doExport(function onFinish(error, exported, recoverable) {
assert.ok(obj.subject.hasDeterminativeProgress());
assert.equal(error.reason, 'NoFreeRecordFound');
assert.equal(recoverable, false);
assert.equal(1, exported);
done();
});
});
test('Recovering from error updating in IccManager', function(done) {
var contacts = [c1, c2, c1, c2];
var obj = setupManagerAndSubject(contacts);
obj.manager.faulty = true;
obj.subject.doExport(function onFinish(error, exported) {
assert.ok(obj.subject.hasDeterminativeProgress());
assert.equal(contacts.length, obj.updateSpy.callCount);
// We do not have an error
assert.isNull(error);
// The number of exported contacts is not the total
assert.equal(contacts.length / 2, exported);
done();
});
});
test('Updating a Contact that has not a previous iccContactId. URL created',
function(done) {
var obj = setupManagerAndSubject([c1]);
obj.subject.doExport(function onFinish(error, exported) {
assert.deepEqual(c1.url[0], iccContactIdUrl);
done();
});
});
test('Updating a Contact that has a previous iccContactId. URL kept as it is',
function(done) {
var contactToExport = {
url: [
iccContactIdUrl
]
};
var obj = setupManagerAndSubject([contactToExport]);
obj.subject.doExport(function onFinish(error, exported) {
assert.equal(contactToExport.url.length, 1);
assert.deepEqual(contactToExport.url[0], iccContactIdUrl);
done();
});
});
test(
'Updating a Contact that has a previous iccContactId from a different SIM',
function(done) {
var contactToExport = {
url: [{
type: ['source', 'sim']
}]
};
contactToExport.url[0].value = getIccContactUrl('98765', '77777');
var contactToExportOriginalUrl = contactToExport.url[0];
var obj = setupManagerAndSubject([contactToExport]);
obj.subject.doExport(function onFinish(error, exported) {
assert.equal(contactToExport.url.length, 2);
assert.deepEqual(contactToExport.url[0], contactToExportOriginalUrl);
assert.deepEqual(contactToExport.url[1], iccContactIdUrl);
done();
});
});
test('Calling with cancel flag activated', function(done) {
var contacts = [c1, c2, c1, c2, c1];
var exportedContacts = 2;
var callNum = 0;
// Replacing the progress function to allow simulating the click of the
// cancel button on the nth call
var progressMockForCancel = function() {
callNum++;
if (callNum === exportedContacts) {
obj.subject.cancelExport();
}
};
var obj = setupManagerAndSubject(contacts);
obj.subject.setProgressStep(progressMockForCancel);
obj.subject.doExport(function onFinish(error, exported) {
assert.equal(exportedContacts, obj.updateSpy.callCount);
assert.isNull(error);
assert.equal(exportedContacts, exported);
done();
});
});
});

View file

@ -0,0 +1,455 @@
'use strict';
/* global fb */
/* global MockDatastore */
/* global MockNavigatorDatastore */
/* global MockPhoneNumberService */
/* global SimplePhoneMatcher */
require('/shared/js/lazy_loader.js');
require('/shared/js/simple_phone_matcher.js');
require('/shared/js/fb/fb_request.js');
require('/shared/js/contacts/import/facebook/fb_data.js');
require('/shared/test/unit/mocks/mock_navigator_datastore.js');
require('/shared/test/unit/mocks/mock_moz_phone_number_service.js');
var realDatastore, realPhoneNumberService;
if (!window.realDatastore) {
window.realDatastore = null;
}
if (!window.realPhoneNumberService) {
window.realPhoneNumberService = null;
}
suite('Facebook datastore suite', function() {
var mockUid = '456789';
var mockTel = '+34676767676';
function createFbContact(uid, name, tel) {
return {
uid: uid,
name: [name],
tel: [{
type: ['other'],
value: tel
}
]
};
}
var MockFbFriendData = createFbContact(mockUid, 'Jose Manuel', mockTel);
function assertRemove(obj) {
var uid = obj.uid;
var index = fb.contacts.dsIndex;
assert.isUndefined(MockDatastore._records[uid]);
var variants = SimplePhoneMatcher.generateVariants(obj.tel[0].value);
variants.forEach(function(aTelVariant) {
assert.isUndefined(index.byTel[aTelVariant]);
});
}
function assertFound(req) {
var res = req.result;
assert.isNotNull(res);
assert.equal(res.uid, mockUid);
}
function assertNotFound(req) {
var res = req.result;
assert.isNull(res);
}
function assertNewIndex() {
assert.equal(Object.keys(MockDatastore._records).length, 1,
'Datastore must contain the persisted index');
var index = fb.contacts.dsIndex;
// Checking that the stored index is the same as the in memory index
assert.deepEqual(index, MockDatastore._records[1]);
assert.equal(Object.keys(index.byTel).length, 0);
}
function assertAdded(friend) {
var uid = friend.uid;
var index = fb.contacts.dsIndex;
assert.equal(MockDatastore._records[uid].uid, uid);
// Testing that all telVariants have been captured in the index
var mockTelVariants = SimplePhoneMatcher.generateVariants(
friend.tel[0].value);
mockTelVariants.forEach(function(aTelVariant) {
assert.equal(index.byTel[aTelVariant], uid);
});
}
function doRemove(objToRemove, flush, done) {
var toRemoveUid = objToRemove.uid;
var toRemoveTel = objToRemove.tel[0].value;
var saveReq = fb.contacts.save(objToRemove);
saveReq.onsuccess = function() {
// Precondition
assertAdded(objToRemove);
// It is needed to flush as add does not automatically flush the index
var flushReq = fb.contacts.flush();
flushReq.onsuccess = function() {
var req = fb.contacts.remove(toRemoveUid, flush);
req.onsuccess = function() {
if (flush) {
assert.isUndefined(MockDatastore._records['1'].byTel[toRemoveTel]);
}
else {
assert.equal(MockDatastore._records['1'].byTel[toRemoveTel],
toRemoveUid);
}
assertRemove(objToRemove);
done();
};
};
flushReq.onerror = errorNotExpected.bind(flushReq, done);
};
saveReq.onerror = errorNotExpected.bind(saveReq, done);
}
function errorNotExpected(done) {
done(function() {
assert.fail('Error not expected: ' + this.error.name);
});
}
function successNotExpected(done) {
done(function() {
assert.fail('Succcess not expected');
});
}
suiteSetup(function() {
realDatastore = navigator.getDataStores;
navigator.getDataStores = MockNavigatorDatastore.getDataStores;
MockNavigatorDatastore._records = {};
realPhoneNumberService = navigator.mozPhoneNumberService;
navigator.mozPhoneNumberService = MockPhoneNumberService;
});
test('Initialization phase and index creation', function(done) {
fb.contacts.init(function() {
// When initialized index must be created
assertNewIndex();
// Testing that the DS is correctly assigned
assert.equal(MockDatastore.name, fb.contacts.datastore.name);
done();
}, function(err) {
done(function() {
assert.fail('Error reported by initialization!: ' + err);
});
});
});
test('Adding to the datastore indexes data correctly', function(done) {
var req = fb.contacts.save(MockFbFriendData);
req.onsuccess = function() {
assertAdded(MockFbFriendData);
done();
};
req.onerror = errorNotExpected.bind(req, done);
});
test('Adding to the datastore an existing obj raises error', function(done) {
var req = fb.contacts.save(MockFbFriendData);
req.onsuccess = successNotExpected.bind(null, done);
req.onerror = function() {
assert.equal(req.error.name, fb.contacts.ALREADY_EXISTS);
done();
};
});
test('Retrieving a FB Friend from the datastore. By tel', function(done) {
var req = fb.contacts.getByPhone(mockTel);
req.onsuccess = function() {
assertFound(req);
done();
};
req.onerror = errorNotExpected.bind(req, done);
});
test('Retrieving a FB Friend from the datastore. By tel not normalized',
function(done) {
var req = fb.contacts.getByPhone('67(67)67-67 6');
req.onsuccess = function() {
assertFound(req);
done();
};
req.onerror = errorNotExpected.bind(req, done);
});
test('Retrieving a FB Friend from the datastore. By tel. Not found',
function(done) {
var req = fb.contacts.getByPhone('987654321');
req.onsuccess = function() {
assertNotFound(req);
done();
};
req.onerror = errorNotExpected.bind(req, done);
});
test('Retrieving a FB Friend from the datastore. By uid', function(done) {
var req = fb.contacts.get(mockUid);
req.onsuccess = function() {
assertFound(req);
done();
};
req.onerror = errorNotExpected.bind(req, done);
});
test('Retrieving a FB Friend from the datastore by UID. Not found',
function(done) {
var req = fb.contacts.get('hhhhh');
req.onsuccess = function() {
assertNotFound(req);
done();
};
req.onerror = errorNotExpected.bind(req, done);
});
test('Removing a FB Friend from the datastore. No flush', function(done) {
var objToRemove = createFbContact('678903', 'Carlos', '+34699999888');
doRemove(objToRemove, false, done);
});
test('Removing a FB Friend from the datastore. Not Found', function(done) {
var objToRemove = createFbContact('99999', 'Carlos', '+34699999888');
var req = fb.contacts.remove(objToRemove);
req.onsuccess = successNotExpected.bind(null, done);
req.onerror = function() {
assert.equal(req.error.name, fb.contacts.UID_NOT_FOUND);
done();
};
});
test('Flushing the data', function(done) {
var req = fb.contacts.flush();
req.onsuccess = function() {
// There has been a flush, thus the "persistent" index is up to date
assert.deepEqual(fb.contacts.dsIndex, MockDatastore._records[1]);
done();
};
req.onerror = errorNotExpected.bind(req, done);
});
test('Removing a FB Friend from the datastore. Flush', function(done) {
var objToRemove = createFbContact('999999', 'Pedro', '+34611789654');
doRemove(objToRemove, true, done);
});
test('Updating a FB friend from the datastore', function(done) {
var updatedObj = createFbContact(MockFbFriendData.uid,
MockFbFriendData.name, '+34657890876');
var req = fb.contacts.update(updatedObj);
req.onsuccess = function() {
var index = fb.contacts.dsIndex;
assert.deepEqual(updatedObj,
MockDatastore._records[MockFbFriendData.uid]);
var telVariants = SimplePhoneMatcher.generateVariants(
updatedObj.tel[0].value);
// Testing that all telVariants have been captured in the index
telVariants.forEach(function(aTelVariant) {
assert.equal(index.byTel[aTelVariant], MockFbFriendData.uid);
});
var telMockVariants = SimplePhoneMatcher.generateVariants(
MockFbFriendData.tel[0].value);
// Testing that reindexing was ok
telMockVariants.forEach(function(aTelVariant) {
assert.isUndefined(index.byTel[aTelVariant]);
});
done();
};
req.onerror = errorNotExpected.bind(req, done);
});
test('Updating a FB friend from the datastore. UID does not exist',
function(done) {
var updatedObj = createFbContact('9999',
MockFbFriendData.name, '+34657890876');
var req = fb.contacts.update(updatedObj);
successNotExpected.bind(null, done);
req.onerror = function() {
assert.equal(req.error.name, 'UIDNotFound');
done();
};
});
test('Clearing all FB Data', function(done) {
var clearReq = fb.contacts.clear();
clearReq.onsuccess = function() {
assertNewIndex();
done();
};
clearReq.onerror = errorNotExpected.bind(clearReq, done);
});
test('If datastore is not found error is reported correctly', function(done) {
MockNavigatorDatastore._notFound = true;
MockDatastore._inError = false;
fb.contacts.restart();
fb.contacts.init(successNotExpected.bind(null, done),
function(err) {
assert.equal(err.name, 'DatastoreNotFound');
done();
});
});
test('Retrieving a FB Friend from the datastore by UID. DS is in Error',
function(done) {
MockNavigatorDatastore._notFound = false;
MockDatastore._inError = true;
fb.contacts.restart();
var req = fb.contacts.get('hhhhh');
req.onsuccess = successNotExpected.bind(null, done);
req.onerror = function() {
assert.equal(req.error.name, 'UnknownError');
done();
};
});
test('Retrieving a FB Friend from the datastore by tel. DS is in Error',
function(done) {
MockNavigatorDatastore._notFound = false;
MockDatastore._inError = true;
var req = fb.contacts.getByPhone('hhhhh');
req.onsuccess = successNotExpected.bind(null, done);
req.onerror = function() {
assert.equal(req.error.name, 'UnknownError');
done();
};
});
test('Adding a FB Friend to the Datastore. DS is in Error',
function(done) {
MockNavigatorDatastore._notFound = false;
MockDatastore._inError = true;
var req = fb.contacts.save(MockFbFriendData);
req.onsuccess = successNotExpected.bind(null, done);
req.onerror = function() {
assert.equal(req.error.name, 'UnknownError');
done();
};
});
test('Updating a FB Friend to the Datastore. DS is in Error',
function(done) {
MockNavigatorDatastore._notFound = false;
MockDatastore._inError = true;
var req = fb.contacts.update({});
req.onsuccess = successNotExpected.bind(null, done);
req.onerror = function() {
assert.equal(req.error.name, 'UnknownError');
done();
};
});
test('Removing a FB Friend from the Datastore. DS is in Error',
function(done) {
MockNavigatorDatastore._notFound = false;
MockDatastore._inError = true;
var req = fb.contacts.remove('45678');
req.onsuccess = successNotExpected.bind(null, done);
req.onerror = function() {
assert.equal(req.error.name, 'UnknownError');
done();
};
});
suite('Initialization phase', function() {
setup(function() {
this.sinon.useFakeTimers();
// other tests are not independent, so let's restart in setup too
fb.contacts.restart();
});
teardown(function() {
fb.contacts.restart();
});
test('is bypassed if DataStore API is unavailable',
function() {
var savedAPI = navigator.getDataStores;
navigator.getDataStores = null;
var success = sinon.stub();
var error = sinon.stub();
fb.contacts.init(success, error);
this.sinon.clock.tick();
sinon.assert.notCalled(success);
sinon.assert.calledWith(error, { name: 'DatastoreNotFound' });
navigator.getDataStores = savedAPI;
});
});
suiteTeardown(function() {
navigator.getDataStores = realDatastore;
navigator.mozPhoneNumberService = realPhoneNumberService;
});
});

View file

@ -0,0 +1,172 @@
'use strict';
/* global fb */
/* global MockFbContactsObj */
/* global MockMozContactsObj */
require('/shared/js/fb/fb_reader_utils.js');
require('/shared/js/fb/fb_request.js');
require('/shared/test/unit/mocks/mock_fb_data_reader.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
var realFbContacts, realMozContacts;
if (!window.realFbContacts) {
window.realFbContacts = null;
}
if (!window.realMozContacts) {
window.realMozContacts = null;
}
suite('Facebook data reader utils suite', function() {
var uid = '999999';
var localName = 'José Carlos Fdez Avila';
var localGivenName = 'José Carlos';
var localFamilyName = 'Fdez Avila';
var localNumber = '676767676';
var fbNumber = '+34655555555';
var localContact = {
id: '12345',
name: [localName],
givenName: [localGivenName],
familyName: [localFamilyName],
category: ['favorite', 'facebook', 'fb_not_linked', uid],
tel: [{
type: ['mobile'],
value: localNumber
}]
};
var fbFriendData = {
uid: uid,
name: ['Carlos Fdez'],
givenName: ['Carlos'],
familyName: ['Fdez'],
tel: [{
type: ['personal'],
value: fbNumber
}],
email: [{
type: ['personal'],
value: 'jj@@jj.com'
}],
photo: [{}]
};
suiteSetup(function() {
realFbContacts = fb.contacts;
fb.contacts = new MockFbContactsObj(fbFriendData);
realMozContacts = navigator.mozContacts;
navigator.mozContacts = new MockMozContactsObj([localContact]);
});
suiteTeardown(function() {
fb.contacts = realFbContacts;
navigator.mozContacts = realMozContacts;
});
test('It is Facebook Contact', function() {
assert.isTrue(fb.isFbContact(localContact));
});
test('It is not Facebook Contact', function() {
var currentCat = localContact.category;
localContact.category = null;
assert.isTrue(!fb.isFbContact(localContact));
localContact.category = currentCat;
});
test('Get Friend UID', function() {
assert.equal(fb.getFriendUid(localContact), uid);
});
test('It is Facebook Contact: Not linked', function() {
assert.isTrue(fb.isFbContact(localContact));
assert.isFalse(fb.isFbLinked(localContact));
});
test('Get LinkedTo. Not Linked', function() {
assert.isUndefined(fb.getLinkedTo(localContact));
});
test('Is Facebook Contact: Linked', function() {
localContact.category[2] = 'fb_linked';
assert.isTrue(fb.isFbLinked(localContact));
});
test('Get LinkedTo. Linked', function() {
localContact.category[2] = 'fb_linked';
assert.equal(fb.getLinkedTo(localContact), uid);
});
function assertFbMergedData(result) {
assert.equal(result.givenName[0], localGivenName);
assert.equal(result.familyName[0], localFamilyName);
assert.equal(result.name[0], localName);
assert.isTrue(Array.isArray(result.tel) && result.tel.length === 2);
assert.equal(result.tel[0].value, localNumber);
assert.equal(result.tel[1].value, fbNumber);
assert.isTrue(Array.isArray(result.photo) && result.photo.length === 1);
}
function errorNotExpected(done) {
done(function() {
assert.fail('Error not expected: ' + this.error.name);
});
}
function successNotExpected(done) {
done(function() {
assert.fail('Succcess not expected');
});
}
test('Get Data from Facebook. Linked Contact', function(done) {
var req = fb.getData(localContact);
req.onsuccess = function() {
done(function() {
var result = req.result;
assertFbMergedData(result);
});
};
req.onerror = errorNotExpected.bind(req, done);
});
test('Get Contact by Number', function(done) {
fb.getContactByNumber(fbNumber, function success(contact) {
done(function() {
assertFbMergedData(contact);
});
}, function error(err) {
(errorNotExpected.bind(err, done))();
});
});
test('Get Contact by Number. In error', function(done) {
fb.contacts.inError = true;
fb.getContactByNumber(fbNumber, function success(contact) {
successNotExpected(done);
},
function error(err) {
done(function() {
assert.equal(err.name, 'UnknownError');
});
});
});
test('Get Data from Facebook. In Error', function(done) {
var req = fb.getData(localContact);
req.onsuccess = successNotExpected.bind(null, done);
req.onerror = function() {
assert.equal(req.error.name, 'UnknownError');
done();
};
});
});

View file

@ -0,0 +1,143 @@
'use strict';
/* global MockImportStatusData, fb */
require('/shared/js/contacts/import/facebook/fb_contact_utils.js');
require('/shared/js/contacts/import/facebook/fb_utils.js');
require('/shared/js/fb/fb_request.js');
requireApp('communications/contacts/test/unit/mock_import_status_data.js');
suite('Fb Utils Tests', function() {
var subject;
var messageCallback;
var pendingSchedule = false, timesToExecute = 0;
var realImportStatusData;
// We need this as sinon.useFaketimers does not support partial stub of
// setInterval and we need setTimeout to work as usual
function tick(times) {
pendingSchedule = true;
timesToExecute = times;
}
function resolveLogout(callback) {
if (typeof callback !== 'function') {
return;
}
window.setTimeout(callback, 0, {
origin: location.origin,
data: 'closed',
stopImmediatePropagation: function() {}
});
}
suiteSetup(function() {
subject = fb.utils;
sinon.stub(window, 'open', function() {
resolveLogout(messageCallback);
return {
closed: false
};
});
sinon.stub(window, 'addEventListener', function(event, cb) {
if (event === 'message') {
messageCallback = cb;
}
});
sinon.stub(window, 'setInterval', function(cb, interval) {
if (pendingSchedule === true) {
for (var j = 0; j < timesToExecute; j++) {
window.setTimeout(cb);
}
}
pendingSchedule = false; timesToExecute = 0;
return 1;
});
realImportStatusData = window.ImportStatusData;
window.ImportStatusData = MockImportStatusData;
});
suiteTeardown(function() {
window.open.restore();
window.addEventListener.restore();
window.setInterval.restore();
window.ImportStatusData = realImportStatusData;
});
setup(function() {
MockImportStatusData.keys = {
'tokenData': {
access_token: '1234'
}
};
});
test('Logout, everything goes well', function(done) {
var req = subject.logout();
req.onsuccess = function(e) {
done(function() {
assert.ok('everything went well');
});
};
req.onerror = done.bind(null, req.error);
tick(1);
});
test('Logout, timeout happens. Error raised', function(done) {
window.open.restore();
sinon.stub(window, 'open', function() {
return {
closed: false
};
});
var req = subject.logout();
req.onsuccess = done;
req.onerror = function() {
done(function() {
assert.equal(req.error, 'Timeout');
});
};
tick(10);
});
test('Logout, window is closed. Error raised', function(done) {
window.open.restore();
sinon.stub(window, 'open', function() {
return {
closed: true
};
});
var req = subject.logout();
// If onsuccess is called done is called with params and the test fails
req.onsuccess = done;
req.onerror = function() {
done(function() {
assert.equal(req.error, 'UserCancelled');
});
};
tick(1);
});
});

View file

@ -0,0 +1,8 @@
'use strict';
/* exported triggerEvent */
var triggerEvent = function(element, eventName) {
var event = document.createEvent('HTMLEvents');
event.initEvent(eventName, true, true);
element.dispatchEvent(event);
};

View file

@ -0,0 +1,164 @@
'use strict';
/* globals MocksHelper, MockLinkHtml, ImageLoader, UIEvent */
require('/shared/js/contacts/utilities/image_loader.js');
requireApp('communications/contacts/test/unit/mock_link.html.js');
require('/shared/test/unit/mocks/mock_image.js');
var mocksHelperForImageLoader = new MocksHelper([
'Image'
]).init();
suite('Image Loader Test Suite >', function() {
var mocksHelper = mocksHelperForImageLoader;
var imageSpy, imgLoader;
mocksHelper.attachTestHelpers();
setup(function() {
imageSpy = this.sinon.spy(window, 'Image');
});
suite('imgsLoading balance >', function() {
var stopSpy, item;
suiteSetup(function() {
document.body.innerHTML = MockLinkHtml;
item = document.querySelector('#friends-list');
imgLoader = new ImageLoader('#mainContent',
'.block-item:not([data-uuid="#uid#"])');
});
setup(function() {
stopSpy = this.sinon.spy(window, 'stop');
});
suiteTeardown(function() {
imgLoader.destroy();
});
function simulateImageCallback(evt) {
imgLoader.defaultLoad(item);
imageSpy.lastCall.thisValue.triggerEvent(evt);
}
function dispatchScroll() {
var event = new UIEvent('scroll', {
view: window,
detail: 0
});
document.querySelector('#mainContent').dispatchEvent(event);
}
test('defaultLoadImage calls new Image()', function() {
imgLoader.defaultLoad(item);
sinon.assert.calledOnce(imageSpy);
});
test('item marked as visited', function() {
simulateImageCallback('onload');
assert.equal('true', item.dataset.visited);
simulateImageCallback('onabort');
assert.equal('false', item.dataset.visited);
simulateImageCallback('onerror');
assert.equal('false', item.dataset.visited);
});
test('window.stop() not called without scroll event', function() {
simulateImageCallback('onload');
sinon.assert.callCount(stopSpy, 0);
simulateImageCallback('onabort');
sinon.assert.callCount(stopSpy, 0);
simulateImageCallback('onerror');
sinon.assert.callCount(stopSpy, 0);
});
test('window.stop() called with one pending request', function() {
imgLoader.defaultLoad(item);
dispatchScroll();
sinon.assert.callCount(stopSpy, 1);
});
test('window.stop() not called after onload', function() {
imgLoader.defaultLoad(item);
imageSpy.lastCall.thisValue.triggerEvent('onload');
dispatchScroll();
sinon.assert.callCount(stopSpy, 0);
});
test('window.stop() not called after onabort and onerror', function() {
imgLoader.defaultLoad(item);
imageSpy.lastCall.thisValue.triggerEvent('onabort');
dispatchScroll();
sinon.assert.callCount(stopSpy, 0);
imgLoader.defaultLoad(item);
imageSpy.lastCall.thisValue.triggerEvent('onerror');
dispatchScroll();
sinon.assert.callCount(stopSpy, 0);
});
test('window.stop() not called after onload and onerror', function() {
imgLoader.defaultLoad(item);
imageSpy.lastCall.thisValue.triggerEvent('onload');
imgLoader.defaultLoad(item);
imageSpy.lastCall.thisValue.triggerEvent('onerror');
dispatchScroll();
sinon.assert.callCount(stopSpy, 0);
});
test('image loader not called anymore after unloading', function() {
var containerSpy = this.sinon.spy(document.querySelector('#mainContent'),
'removeEventListener');
var documentSpy = this.sinon.spy(document, 'removeEventListener');
imgLoader.defaultLoad(item);
imgLoader.unload();
sinon.assert.callCount(documentSpy, 1);
sinon.assert.callCount(containerSpy, 1);
documentSpy.restore();
containerSpy.restore();
});
test('image loader not called anymore after pausing', function() {
var containerSpy = this.sinon.spy(document.querySelector('#mainContent'),
'removeEventListener');
var documentSpy = this.sinon.spy(document, 'removeEventListener');
imgLoader.defaultLoad(item);
window.dispatchEvent(new CustomEvent('image-loader-pause'));
sinon.assert.callCount(documentSpy, 1);
sinon.assert.callCount(containerSpy, 1);
documentSpy.restore();
containerSpy.restore();
});
});
suite('imgsLoader resuming >', function() {
suiteSetup(function() {
document.body.innerHTML =
'<ol>' +
'<li><span data-type="img" data-src="http://www.a.com"></span></li>' +
'</ol>';
imgLoader = new ImageLoader('ol', 'li');
});
suiteTeardown(function() {
imgLoader.destroy();
});
test('resuming calls new Image()', function() {
window.dispatchEvent(new CustomEvent('image-loader-resume'));
sinon.assert.calledOnce(imageSpy);
});
});
});

View file

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

View file

@ -0,0 +1,65 @@
'use strict';
/* globals MockNavigatorDatastore, MockDatastoreObj, ContactsDatastore */
require('/shared/js/contacts/contacts_datastore.js');
require('/shared/test/unit/mocks/mock_navigator_datastore.js');
suite('Contacts Datastore', function() {
var realDatastore;
var datastoreOwner = 'app://gmailprovider.gaiamobile.org/manifest.webapp';
suiteSetup(function() {
var datastore1 = new MockDatastoreObj('contacts', datastoreOwner, null);
MockNavigatorDatastore._datastores = [
datastore1
];
realDatastore = navigator.getDataStores;
navigator.getDataStores = MockNavigatorDatastore.getDataStores;
});
suiteTeardown(function() {
navigator.getDataStores = realDatastore;
});
test('it provides the datastore', function(done) {
var subject = new ContactsDatastore(datastoreOwner);
subject.getStore().then(function(store) {
done(function() {
assert.equal(store.owner, datastoreOwner);
});
}).catch (done);
});
test('if the provider name is incorrect promise is rejected', function(done) {
var subject = new ContactsDatastore('undefinedprovider');
subject.getStore().then(function(store) {
done('failed');
}).catch (function(error) {
done(function() {
assert.equal(error.name, 'DatastoreNotFound');
});
});
});
test('if navigator.getDatastores is not available promise is rejected',
function(done) {
navigator.getDataStores = null;
var subject = new ContactsDatastore(datastoreOwner);
subject.getStore().then(function(store) {
done('failed');
}).catch (function(error) {
done(function() {
assert.equal(error.name, 'DatastoreNotEnabled');
});
});
});
});

View file

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

View file

@ -0,0 +1,352 @@
/* globals GmailConnector, MockGoogleEntry, MockGoogleEntryInvalidDate,
MockGoogleEntryNoName, MockGoogleGroups, MockGoogleListing, MockRest,
MockGoogleEntryNoFirstName */
'use strict';
require('/shared/js/contacts/import/gmail/gmail_connector.js');
requireApp('communications/contacts/test/unit/import/gmail/mock_contact1.js');
requireApp('communications/contacts/test/unit/import/gmail/mock_listing.js');
requireApp('communications/contacts/test/unit/import/mock_rest.js');
requireApp('communications/contacts/test/unit/import/gmail/mock_groups.js');
if (!window.Rest) {
window.Rest = null;
}
suite('Gmail Connector', function() {
var subject,
END_POINT = 'https://www.google.com/m8/feeds/contacts/' +
'default/full/?max-results=10000',
GROUP_ID = 'https://www.google.com/m8/feeds/groups/' +
'mepartoconmigo%40gmail.com/base/6';
suiteSetup(function() {
subject = GmailConnector;
window.Rest = MockRest;
});
suiteTeardown(function() {
window.Rest = null;
});
suite('Parsing', function() {
test('Check contacts listing from feed', function(done) {
var callbacks = {
'success': function onSuccess(result) {
assert.isNotNull(result);
assert.isNotNull(result.data);
assert.lengthOf(result.data, 2);
done();
},
'error': function onError(error) {
assert.fail('We were no suppose to get an error',
'We got an error while get');
},
'timeout': function onTimeout(to) {
assert.fail('We were no suppose to get a timeout',
'We got a timeout while get');
}
};
var contactsListUrl = END_POINT + '&group=' + GROUP_ID;
var restConfigure = {
'type': 'success',
'https://www.google.com/m8/feeds/groups/default/full/': MockGoogleGroups
};
restConfigure[contactsListUrl] = MockGoogleListing;
window.Rest.configure(restConfigure);
subject.listAllContacts('123456789', callbacks);
});
test('Parse for display', function() {
var contact = subject.gContactToJson(MockGoogleEntry);
var result = subject.adaptDataForShowing(contact);
assert.isNotNull(result);
assert.equal(result.uid,
'http://www.google.com/m8/feeds/contacts/' +
'mepartoconmigo%40gmail.com/base/2fc27a388c2bd974');
assert.deepEqual(result.givenName, ['My']);
assert.deepEqual(result.familyName, ['Contact']);
assert.equal(result.email1, 'juan@palomo.es');
});
test('Parse for display without name', function() {
var contact = subject.gContactToJson(MockGoogleEntryNoName);
var result = subject.adaptDataForShowing(contact);
assert.isNotNull(result);
assert.equal(result.uid,
'http://www.google.com/m8/feeds/contacts/' +
'mepartoconmigo%40gmail.com/base/2fc27a388c2bd974');
assert.deepEqual(result.givenName, 'juan@palomo.es');
assert.equal(result.email1, 'juan@palomo.es');
});
test('Parse for display with invalid birthday date', function() {
var contact = subject.gContactToJson(MockGoogleEntryInvalidDate);
var result = subject.adaptDataForShowing(contact);
assert.isNotNull(result);
assert.equal(result.uid,
'http://www.google.com/m8/feeds/contacts/' +
'mepartoconmigo%40gmail.com/base/2fc27a388c2bd974');
assert.isUndefined(result.bday);
});
test('Parse for display with only last name', function() {
var contact = subject.gContactToJson(MockGoogleEntryNoFirstName);
var result = subject.adaptDataForShowing(contact);
assert.isNotNull(result);
assert.equal(result.uid,
'http://www.google.com/m8/feeds/contacts/' +
'mepartoconmigo%40gmail.com/base/2fc27a388c2bd974');
assert.isUndefined(result.bday);
});
test('Parse for saving', function() {
var contact = subject.gContactToJson(MockGoogleEntry);
var result = subject.adaptDataForSaving(contact);
assert.isNotNull(result);
assert.isNotNull(result.givenName);
assert.lengthOf(result.givenName, 1);
assert.equal(result.givenName[0], 'My');
assert.isNotNull(result.familyName);
assert.lengthOf(result.familyName, 1);
assert.equal(result.familyName[0], 'Contact');
assert.isNotNull(result.email);
assert.lengthOf(result.email, 2);
assert.equal('juan@palomo.es', result.email[0].value);
assert.equal('home', result.email[0].type);
assert.equal('workemail@email.com', result.email[1].value);
assert.equal('work', result.email[1].type);
assert.isNotNull(result.adr);
assert.lengthOf(result.adr, 1);
assert.equal('This is the Street', result.adr[0].streetAddress);
assert.equal('The City', result.adr[0].locality);
assert.equal('The State', result.adr[0].region);
assert.equal('The Zip', result.adr[0].postalCode);
assert.equal('The Country', result.adr[0].countryName);
assert.isNotNull(result.tel);
assert.lengthOf(result.tel, 1);
assert.equal('+1555111333444222', result.tel[0].value);
assert.equal('work', result.tel[0].type);
assert.isNotNull(result.org);
assert.lengthOf(result.org, 1);
assert.equal('The Company', result.org[0]);
assert.isNotNull(result.jobTitle);
assert.lengthOf(result.jobTitle, 1);
assert.equal('Title in Company', result.jobTitle[0]);
assert.isNotNull(result.bday);
assert.deepEqual(new Date('1990-01-01'), result.bday);
assert.isNotNull(result.note);
assert.lengthOf(result.note, 1);
assert.equal('This is a Note', result.note[0]);
assert.isNotNull(result.category);
assert.lengthOf(result.category, 1);
assert.equal('gmail', result.category[0]);
assert.isNotNull(result.url);
assert.lengthOf(result.url, 1);
assert.lengthOf(result.url[0].type, 1);
assert.equal('source', result.url[0].type[0]);
assert.equal('urn:service:gmail:uid:' +
'http://www.google.com/m8/feeds/contacts/' +
'mepartoconmigo%40gmail.com/base/2fc27a388c2bd974',
result.url[0].value);
});
test('Parse for saving without name', function() {
var contact = subject.gContactToJson(MockGoogleEntryNoName);
var result = subject.adaptDataForSaving(contact);
assert.isNotNull(result);
assert.isTrue(!result.givenName);
assert.isTrue(!result.familyName);
assert.isNotNull(result.email);
assert.lengthOf(result.email, 2);
assert.equal('juan@palomo.es', result.email[0].value);
assert.equal('home', result.email[0].type);
assert.equal('workemail@email.com', result.email[1].value);
assert.equal('work', result.email[1].type);
assert.isNotNull(result.adr);
assert.lengthOf(result.adr, 1);
assert.equal('This is the Street', result.adr[0].streetAddress);
assert.equal('The City', result.adr[0].locality);
assert.equal('The State', result.adr[0].region);
assert.equal('The Zip', result.adr[0].postalCode);
assert.equal('The Country', result.adr[0].countryName);
assert.isNotNull(result.tel);
assert.lengthOf(result.tel, 1);
assert.equal('+1555111333444222', result.tel[0].value);
assert.equal('work', result.tel[0].type);
assert.isNotNull(result.org);
assert.lengthOf(result.org, 1);
assert.equal('The Company', result.org[0]);
assert.isNotNull(result.jobTitle);
assert.lengthOf(result.jobTitle, 1);
assert.equal('Title in Company', result.jobTitle[0]);
assert.isNotNull(result.bday);
assert.deepEqual(new Date('1990-01-01'), result.bday);
assert.isNotNull(result.note);
assert.lengthOf(result.note, 1);
assert.equal('This is a Note', result.note[0]);
assert.isNotNull(result.category);
assert.lengthOf(result.category, 1);
assert.equal('gmail', result.category[0]);
assert.isNotNull(result.url);
assert.lengthOf(result.url, 1);
assert.lengthOf(result.url[0].type, 1);
assert.equal('source', result.url[0].type[0]);
assert.equal('urn:service:gmail:uid:' +
'http://www.google.com/m8/feeds/contacts/' +
'mepartoconmigo%40gmail.com/base/2fc27a388c2bd974',
result.url[0].value);
});
test('Parse for saving with invalid birthday date', function() {
var contact = subject.gContactToJson(MockGoogleEntryInvalidDate);
var result = subject.adaptDataForSaving(contact);
assert.isNotNull(result);
assert.isNotNull(result.givenName);
assert.lengthOf(result.givenName, 1);
assert.equal(result.givenName[0], 'My');
assert.isNotNull(result.familyName);
assert.lengthOf(result.familyName, 1);
assert.equal(result.familyName[0], 'Contact');
assert.isNotNull(result.email);
assert.lengthOf(result.email, 2);
assert.equal('juan@palomo.es', result.email[0].value);
assert.equal('home', result.email[0].type);
assert.equal('workemail@email.com', result.email[1].value);
assert.equal('work', result.email[1].type);
assert.isNotNull(result.adr);
assert.lengthOf(result.adr, 1);
assert.equal('This is the Street', result.adr[0].streetAddress);
assert.equal('The City', result.adr[0].locality);
assert.equal('The State', result.adr[0].region);
assert.equal('The Zip', result.adr[0].postalCode);
assert.equal('The Country', result.adr[0].countryName);
assert.isNotNull(result.tel);
assert.lengthOf(result.tel, 1);
assert.equal('+1555111333444222', result.tel[0].value);
assert.equal('work', result.tel[0].type);
assert.isNotNull(result.org);
assert.lengthOf(result.org, 1);
assert.equal('The Company', result.org[0]);
assert.isNotNull(result.jobTitle);
assert.lengthOf(result.jobTitle, 1);
assert.equal('Title in Company', result.jobTitle[0]);
assert.isUndefined(result.bday);
assert.isNotNull(result.note);
assert.lengthOf(result.note, 1);
assert.equal('This is a Note', result.note[0]);
assert.isNotNull(result.category);
assert.lengthOf(result.category, 1);
assert.equal('gmail', result.category[0]);
assert.isNotNull(result.url);
assert.lengthOf(result.url, 1);
assert.lengthOf(result.url[0].type, 1);
assert.equal('source', result.url[0].type[0]);
assert.equal('urn:service:gmail:uid:' +
'http://www.google.com/m8/feeds/contacts/' +
'mepartoconmigo%40gmail.com/base/2fc27a388c2bd974',
result.url[0].value);
});
test('Parse for saving with no first name', function() {
var contact = subject.gContactToJson(MockGoogleEntryNoFirstName);
var result = subject.adaptDataForSaving(contact);
assert.isNotNull(result);
assert.isTrue(!result.givenName);
assert.isNotNull(result.familyName);
assert.lengthOf(result.familyName, 1);
assert.equal(result.familyName[0], 'Contact');
assert.isNotNull(result.email);
assert.lengthOf(result.email, 2);
assert.equal('juan@palomo.es', result.email[0].value);
assert.equal('home', result.email[0].type);
assert.equal('workemail@email.com', result.email[1].value);
assert.equal('work', result.email[1].type);
assert.isNotNull(result.adr);
assert.lengthOf(result.adr, 1);
assert.equal('This is the Street', result.adr[0].streetAddress);
assert.equal('The City', result.adr[0].locality);
assert.equal('The State', result.adr[0].region);
assert.equal('The Zip', result.adr[0].postalCode);
assert.equal('The Country', result.adr[0].countryName);
assert.isNotNull(result.tel);
assert.lengthOf(result.tel, 1);
assert.equal('+1555111333444222', result.tel[0].value);
assert.equal('work', result.tel[0].type);
assert.isNotNull(result.org);
assert.lengthOf(result.org, 1);
assert.equal('The Company', result.org[0]);
assert.isNotNull(result.jobTitle);
assert.lengthOf(result.jobTitle, 1);
assert.equal('Title in Company', result.jobTitle[0]);
assert.isNotNull(result.bday);
assert.deepEqual(new Date('1990-01-01'), result.bday);
assert.isNotNull(result.note);
assert.lengthOf(result.note, 1);
assert.equal('This is a Note', result.note[0]);
assert.isNotNull(result.category);
assert.lengthOf(result.category, 1);
assert.equal('gmail', result.category[0]);
assert.isNotNull(result.url);
assert.lengthOf(result.url, 1);
assert.lengthOf(result.url[0].type, 1);
assert.equal('source', result.url[0].type[0]);
assert.equal('urn:service:gmail:uid:' +
'http://www.google.com/m8/feeds/contacts/' +
'mepartoconmigo%40gmail.com/base/2fc27a388c2bd974',
result.url[0].value);
});
});
});

View file

@ -0,0 +1,46 @@
'use strict';
/* exported MockGoogleEntry, MockGoogleEntryNoName,
MockGoogleEntryInvalidDate, MockGoogleEntryNoFirstName */
// refer to mock_contact1.xml for the entry XML we are getting.
var MockGoogleEntry = (function MockGoogleEntry() {
var req = new XMLHttpRequest();
req.open('GET', '/contacts/test/unit/import/gmail/mock_contact1.xml', false);
req.send(null);
var entryBuffer = req.responseText;
var parser = new DOMParser();
return parser.parseFromString(entryBuffer, 'text/xml');
})();
var MockGoogleEntryNoName = (function MockGoogleEntryNoName() {
var req = new XMLHttpRequest();
req.open('GET', '/contacts/test/unit/import/gmail/mock_contact2.xml', false);
req.send(null);
var entryBuffer = req.responseText;
var parser = new DOMParser();
return parser.parseFromString(entryBuffer, 'text/xml');
})();
var MockGoogleEntryInvalidDate = (function MockGoogleEntryWoName() {
var req = new XMLHttpRequest();
req.open('GET', '/contacts/test/unit/import/gmail/mock_contact3.xml', false);
req.send(null);
var entryBuffer = req.responseText;
var parser = new DOMParser();
return parser.parseFromString(entryBuffer, 'text/xml');
})();
var MockGoogleEntryNoFirstName = (function MockGoogleEntryNoFirstName() {
var req = new XMLHttpRequest();
req.open('GET', '/contacts/test/unit/import/gmail/mock_contact4.xml', false);
req.send(null);
var entryBuffer = req.responseText;
var parser = new DOMParser();
return parser.parseFromString(entryBuffer, 'text/xml');
})();

View file

@ -0,0 +1,41 @@
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gContact="http://schemas.google.com/contact/2008" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gd="http://schemas.google.com/g/2005" gd:etag="&quot;RXg9fDVSLit7I2A9WhBSGUsPQgU.&quot;">
<id>http://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/base/2fc27a388c2bd974</id>
<updated>2013-02-27T11:45:14.664Z</updated>
<app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-27T11:45:14.664Z</app:edited>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact" />
<title>My Shiny Contact</title>
<content>This is a Note</content>
<link rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*" href="https://www.google.com/m8/feeds/photos/media/mepartoconmigo%40gmail.com/2fc27a388c2bd974" gd:etag="&quot;LhMgGHAHWit7I2BuPF4FFCt_B0ZEGiYpXwg.&quot;" />
<link rel="self" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/full/2fc27a388c2bd974" />
<link rel="edit" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/full/2fc27a388c2bd974" />
<gd:name>
<gd:fullName>My Shiny Contact</gd:fullName>
<gd:givenName>My</gd:givenName>
<gd:additionalName>Shiny</gd:additionalName>
<gd:familyName>Contact</gd:familyName>
</gd:name>
<gContact:birthday when="1990-01-01" />
<gd:organization rel="http://schemas.google.com/g/2005#other">
<gd:orgName>The Company</gd:orgName>
<gd:orgTitle>Title in Company</gd:orgTitle>
</gd:organization>
<gd:email rel="http://schemas.google.com/g/2005#home" address="juan@palomo.es" primary="true" />
<gd:email rel="http://schemas.google.com/g/2005#work" address="workemail@email.com" />
<gd:im address="instantmessaging" protocol="http://schemas.google.com/g/2005#GOOGLE_TALK" rel="http://schemas.google.com/g/2005#other" />
<gd:phoneNumber rel="http://schemas.google.com/g/2005#work">+1555111333444222</gd:phoneNumber>
<gd:structuredPostalAddress rel="http://schemas.google.com/g/2005#home">
<gd:formattedAddress>This is the Street
The PO Box
The Nieghborhood, The City, The State The Zip
The Country</gd:formattedAddress>
<gd:street>This is the Street</gd:street>
<gd:neighborhood>The Nieghborhood</gd:neighborhood>
<gd:pobox>The PO Box</gd:pobox>
<gd:postcode>The Zip</gd:postcode>
<gd:city>The City</gd:city>
<gd:region>The State</gd:region>
<gd:country>The Country</gd:country>
</gd:structuredPostalAddress>
<gContact:website href="http://www.myurl.com" rel="profile" />
<gContact:groupMembershipInfo deleted="false" href="http://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/base/6" />
</entry>

View file

@ -0,0 +1,35 @@
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gContact="http://schemas.google.com/contact/2008" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gd="http://schemas.google.com/g/2005" gd:etag="&quot;RXg9fDVSLit7I2A9WhBSGUsPQgU.&quot;">
<id>http://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/base/2fc27a388c2bd974</id>
<updated>2013-02-27T11:45:14.664Z</updated>
<app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-27T11:45:14.664Z</app:edited>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact" />
<title>My Shiny Contact</title>
<content>This is a Note</content>
<link rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*" href="https://www.google.com/m8/feeds/photos/media/mepartoconmigo%40gmail.com/2fc27a388c2bd974" gd:etag="&quot;LhMgGHAHWit7I2BuPF4FFCt_B0ZEGiYpXwg.&quot;" />
<link rel="self" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/full/2fc27a388c2bd974" />
<link rel="edit" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/full/2fc27a388c2bd974" />
<gContact:birthday when="1990-01-01" />
<gd:organization rel="http://schemas.google.com/g/2005#other">
<gd:orgName>The Company</gd:orgName>
<gd:orgTitle>Title in Company</gd:orgTitle>
</gd:organization>
<gd:email rel="http://schemas.google.com/g/2005#home" address="juan@palomo.es" primary="true" />
<gd:email rel="http://schemas.google.com/g/2005#work" address="workemail@email.com" />
<gd:im address="instantmessaging" protocol="http://schemas.google.com/g/2005#GOOGLE_TALK" rel="http://schemas.google.com/g/2005#other" />
<gd:phoneNumber rel="http://schemas.google.com/g/2005#work">+1555111333444222</gd:phoneNumber>
<gd:structuredPostalAddress rel="http://schemas.google.com/g/2005#home">
<gd:formattedAddress>This is the Street
The PO Box
The Nieghborhood, The City, The State The Zip
The Country</gd:formattedAddress>
<gd:street>This is the Street</gd:street>
<gd:neighborhood>The Nieghborhood</gd:neighborhood>
<gd:pobox>The PO Box</gd:pobox>
<gd:postcode>The Zip</gd:postcode>
<gd:city>The City</gd:city>
<gd:region>The State</gd:region>
<gd:country>The Country</gd:country>
</gd:structuredPostalAddress>
<gContact:website href="http://www.myurl.com" rel="profile" />
<gContact:groupMembershipInfo deleted="false" href="http://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/base/6" />
</entry>

View file

@ -0,0 +1,41 @@
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gContact="http://schemas.google.com/contact/2008" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gd="http://schemas.google.com/g/2005" gd:etag="&quot;RXg9fDVSLit7I2A9WhBSGUsPQgU.&quot;">
<id>http://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/base/2fc27a388c2bd974</id>
<updated>2013-02-27T11:45:14.664Z</updated>
<app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-27T11:45:14.664Z</app:edited>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact" />
<title>My Shiny Contact</title>
<content>This is a Note</content>
<link rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*" href="https://www.google.com/m8/feeds/photos/media/mepartoconmigo%40gmail.com/2fc27a388c2bd974" gd:etag="&quot;LhMgGHAHWit7I2BuPF4FFCt_B0ZEGiYpXwg.&quot;" />
<link rel="self" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/full/2fc27a388c2bd974" />
<link rel="edit" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/full/2fc27a388c2bd974" />
<gd:name>
<gd:fullName>My Shiny Contact</gd:fullName>
<gd:givenName>My</gd:givenName>
<gd:additionalName>Shiny</gd:additionalName>
<gd:familyName>Contact</gd:familyName>
</gd:name>
<gContact:birthday when="June 24" />
<gd:organization rel="http://schemas.google.com/g/2005#other">
<gd:orgName>The Company</gd:orgName>
<gd:orgTitle>Title in Company</gd:orgTitle>
</gd:organization>
<gd:email rel="http://schemas.google.com/g/2005#home" address="juan@palomo.es" primary="true" />
<gd:email rel="http://schemas.google.com/g/2005#work" address="workemail@email.com" />
<gd:im address="instantmessaging" protocol="http://schemas.google.com/g/2005#GOOGLE_TALK" rel="http://schemas.google.com/g/2005#other" />
<gd:phoneNumber rel="http://schemas.google.com/g/2005#work">+1555111333444222</gd:phoneNumber>
<gd:structuredPostalAddress rel="http://schemas.google.com/g/2005#home">
<gd:formattedAddress>This is the Street
The PO Box
The Nieghborhood, The City, The State The Zip
The Country</gd:formattedAddress>
<gd:street>This is the Street</gd:street>
<gd:neighborhood>The Nieghborhood</gd:neighborhood>
<gd:pobox>The PO Box</gd:pobox>
<gd:postcode>The Zip</gd:postcode>
<gd:city>The City</gd:city>
<gd:region>The State</gd:region>
<gd:country>The Country</gd:country>
</gd:structuredPostalAddress>
<gContact:website href="http://www.myurl.com" rel="profile" />
<gContact:groupMembershipInfo deleted="false" href="http://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/base/6" />
</entry>

View file

@ -0,0 +1,41 @@
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gContact="http://schemas.google.com/contact/2008" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gd="http://schemas.google.com/g/2005" gd:etag="&quot;RXg9fDVSLit7I2A9WhBSGUsPQgU.&quot;">
<id>http://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/base/2fc27a388c2bd974</id>
<updated>2013-02-27T11:45:14.664Z</updated>
<app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-27T11:45:14.664Z</app:edited>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact" />
<title>My Shiny Contact</title>
<content>This is a Note</content>
<link rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*" href="https://www.google.com/m8/feeds/photos/media/mepartoconmigo%40gmail.com/2fc27a388c2bd974" gd:etag="&quot;LhMgGHAHWit7I2BuPF4FFCt_B0ZEGiYpXwg.&quot;" />
<link rel="self" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/full/2fc27a388c2bd974" />
<link rel="edit" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/mepartoconmigo%40gmail.com/full/2fc27a388c2bd974" />
<gd:name>
<gd:fullName>Contact</gd:fullName>
<gd:givenName></gd:givenName>
<gd:additionalName></gd:additionalName>
<gd:familyName>Contact</gd:familyName>
</gd:name>
<gContact:birthday when="1990-01-01" />
<gd:organization rel="http://schemas.google.com/g/2005#other">
<gd:orgName>The Company</gd:orgName>
<gd:orgTitle>Title in Company</gd:orgTitle>
</gd:organization>
<gd:email rel="http://schemas.google.com/g/2005#home" address="juan@palomo.es" primary="true" />
<gd:email rel="http://schemas.google.com/g/2005#work" address="workemail@email.com" />
<gd:im address="instantmessaging" protocol="http://schemas.google.com/g/2005#GOOGLE_TALK" rel="http://schemas.google.com/g/2005#other" />
<gd:phoneNumber rel="http://schemas.google.com/g/2005#work">+1555111333444222</gd:phoneNumber>
<gd:structuredPostalAddress rel="http://schemas.google.com/g/2005#home">
<gd:formattedAddress>This is the Street
The PO Box
The Nieghborhood, The City, The State The Zip
The Country</gd:formattedAddress>
<gd:street>This is the Street</gd:street>
<gd:neighborhood>The Nieghborhood</gd:neighborhood>
<gd:pobox>The PO Box</gd:pobox>
<gd:postcode>The Zip</gd:postcode>
<gd:city>The City</gd:city>
<gd:region>The State</gd:region>
<gd:country>The Country</gd:country>
</gd:structuredPostalAddress>
<gContact:website href="http://www.myurl.com" rel="profile" />
<gContact:groupMembershipInfo deleted="false" href="http://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/base/6" />
</entry>

View file

@ -0,0 +1,23 @@
'use strict';
/* exported MockGoogleGroups */
/*
This mock represents the following google contacts response
for groups of contacts. See mock_groups.xml.
*/
var MockGoogleGroups = (function MockGoogleGroups() {
var req = new XMLHttpRequest();
req.open('GET', '/contacts/test/unit/import/gmail/mock_groups.xml', false);
req.send(null);
var entryBuffer = req.responseText;
var parser = new DOMParser();
var listing = parser.parseFromString(entryBuffer, 'text/xml');
return listing;
})();

View file

@ -0,0 +1 @@
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/' xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:gd='http://schemas.google.com/g/2005' gd:etag='W/&quot;DkUFSHgzeSt7I2A9WhBQGEs.&quot;'><id>mepartoconmigo@gmail.com</id><updated>2013-03-21T11:10:19.681Z</updated><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#group'/><title>arcturus's Contact Groups</title><link rel='alternate' type='text/html' href='http://www.google.com/'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/full'/><link rel='http://schemas.google.com/g/2005#post' type='application/atom+xml' href='https://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/full'/><link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml' href='https://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/full/batch'/><link rel='self' type='application/atom+xml' href='https://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/full?max-results=25'/><author><name>arcturus</name><email>mepartoconmigo@gmail.com</email></author><generator version='1.0' uri='http://www.google.com/m8/feeds'>Contacts</generator><openSearch:totalResults>5</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry gd:etag='&quot;YDwreyM.&quot;'><id>https://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/base/6</id><updated>1970-01-01T00:00:00.000Z</updated><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#group'/><title>System Group: My Contacts</title><content>System Group: My Contacts</content><link rel='self' type='application/atom+xml' href='https://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/full/6'/><gContact:systemGroup id='Contacts'/></entry><entry gd:etag='&quot;YDwreyM.&quot;'><id>https://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/base/d</id><updated>1970-01-01T00:00:00.000Z</updated><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#group'/><title>System Group: Friends</title><content>System Group: Friends</content><link rel='self' type='application/atom+xml' href='https://www.google.com/m8/feeds/groups/mepartoconmigo%40gmail.com/full/d'/><gContact:systemGroup id='Friends'/></entry></feed>

View file

@ -0,0 +1,23 @@
'use strict';
/* exported MockGoogleListing */
/*
This mock represents the following google contacts response
for a list of contacts. See mock_listing.xml.
*/
var MockGoogleListing = (function MockGoogleListing() {
var req = new XMLHttpRequest();
req.open('GET', '/contacts/test/unit/import/gmail/mock_listing.xml', false);
req.send(null);
var entryBuffer = req.responseText;
var parser = new DOMParser();
var listing = parser.parseFromString(entryBuffer, 'text/xml');
return listing;
})();

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:gContact="http://schemas.google.com/contact/2008" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gd="http://schemas.google.com/g/2005" gd:etag="W/&quot;DkENRHY6fyt7I2A9WhBSGUU.&quot;">
<id>someemail@gmail.com</id>
<updated>2013-02-27T17:04:55.817Z</updated>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact" />
<title>someone's Contacts</title>
<link rel="alternate" type="text/html" href="http://www.google.com/" />
<link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/full" />
<link rel="http://schemas.google.com/g/2005#post" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/full" />
<link rel="http://schemas.google.com/g/2005#batch" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/full/batch" />
<link rel="self" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/full?max-results=25" />
<link rel="next" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/full?start-index=26&amp;max-results=25" />
<author>
<name>someone</name>
<email>someemail@gmail.com</email>
</author>
<generator version="1.0" uri="http://www.google.com/m8/feeds">Contacts</generator>
<openSearch:totalResults>2</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>25</openSearch:itemsPerPage>
<entry gd:etag="&quot;RXs7ejVSLit7I2A9WxNRFE4IQAY.&quot;">
<id>http://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/base/1</id>
<updated>2009-09-08T17:58:44.502Z</updated>
<app:edited xmlns:app="http://www.w3.org/2007/app">2009-09-08T17:58:44.502Z</app:edited>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact" />
<title />
<link rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*" href="https://www.google.com/m8/feeds/photos/media/somegoogleemail%40gmail.com/1" />
<link rel="self" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/full/1" />
<link rel="edit" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/full/1" />
<gd:email rel="http://schemas.google.com/g/2005#other" address="another@gmail.com" primary="true" />
<gd:email rel="http://schemas.google.com/g/2005#work" address="work@gmail.com" primary="true" />
</entry>
<entry gd:etag="&quot;RHY4fTNRKyt7I2A9WhJbEkQDQgY.&quot;">
<id>http://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/base/2</id>
<updated>2012-09-22T07:38:45.835Z</updated>
<app:edited xmlns:app="http://www.w3.org/2007/app">2012-09-22T07:38:45.835Z</app:edited>
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact" />
<title>John Doe</title>
<gd:name>
<gd:fullName>John Doe</gd:fullName>
<gd:givenName>John</gd:givenName>
<gd:additionalName></gd:additionalName>
<gd:familyName>Doe</gd:familyName>
</gd:name>
<gContact:birthday when="1990-01-01" />
<link rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*" href="https://www.google.com/m8/feeds/photos/media/somegoogleemail%40gmail.com/2" gd:etag="&quot;UR5VJUMvbCt7I2BcOB46EypYQV4BJGx4YVc.&quot;" />
<link rel="self" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/full/2" />
<link rel="edit" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/somegoogleemail%40gmail.com/full/2" />
</entry>
</feed>

View file

@ -0,0 +1,229 @@
'use strict';
/* global contacts */
/* global LazyLoader */
/* global MyLocks */
/* global MockWakeLock */
/* global MockasyncStorage */
/* global MockMozL10n */
/* global MockSdCard */
/* global MocksHelper */
/* global MockUtils */
/* global MockContactsIndexHtml */
/* global MockNavigatorMozMobileConnections */
/* global MockSimContactsImporter */
/* global MockVCFReader */
/* global MockMozContacts */
/* global MockAdvancedTelemetryHelper */
require('/shared/js/lazy_loader.js');
require('/shared/test/unit/mocks/mock_navigator_moz_mobile_connections.js');
require('/shared/test/unit/mocks/mock_sdcard.js');
require('/shared/test/unit/mocks/mock_vcard_parser.js');
require('/shared/test/unit/mocks/mock_advanced_telemetry_helper.js');
requireApp('communications/contacts/services/contacts.js');
requireApp('communications/contacts/test/unit/mock_contacts_index.html.js');
requireApp('communications/contacts/test/unit/mock_service_extensions.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
requireApp('communications/contacts/test/unit/mock_asyncstorage.js');
requireApp('communications/contacts/test/unit/mock_fb.js');
requireApp('communications/contacts/test/unit/mock_wakelock.js');
requireApp('communications/contacts/test/unit/mock_l10n.js');
requireApp('communications/contacts/test/unit/mock_event_listeners.js');
requireApp('communications/contacts/test/unit/mock_sim_importer.js');
require('/shared/test/unit/mocks/mock_confirm_dialog.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
requireApp('communications/contacts/js/views/settings.js');
requireApp('communications/contacts/js/utilities/icc_handler.js');
requireApp('communications/contacts/js/utilities/sim_dom_generator.js');
requireApp('communications/contacts/js/navigation.js');
if (!window._) { window._ = null; }
if (!window.utils) { window.utils = null; }
if (!navigator.mozMobileConnections) { navigator.mozMobileConnections = null; }
var mocksHelperForContactImport = new MocksHelper([
'ExtServices', 'Contacts', 'fb', 'asyncStorage', 'ConfirmDialog',
'VCFReader', 'WakeLock', 'SimContactsImporter'
]);
mocksHelperForContactImport.init();
suite('Import contacts >', function() {
var mocksHelper = mocksHelperForContactImport;
var real_,
realUtils,
realWakeLock,
realMozMobileConnections,
realMozContacts,
realATH;
setup(function() {
this.sinon.spy(window.utils.overlay, 'showMenu');
});
teardown(function() {
MockasyncStorage.clear();
});
suiteSetup(function(done) {
mocksHelper.suiteSetup();
realMozMobileConnections = navigator.mozMobileConnections;
navigator.mozMobileConnections = MockNavigatorMozMobileConnections;
realWakeLock = navigator.requestWakeLock;
navigator.requestWakeLock = MockWakeLock;
realMozContacts = navigator.mozContacts;
navigator.mozContacts = MockMozContacts;
real_ = window._;
window._ = MockMozL10n.get;
realUtils = window.utils;
window.utils = MockUtils;
window.utils.overlay = {
show: function() {},
showMenu: function() {}
};
window.utils.status = {
show: function() {}
};
window.utils.misc = {
getTimestamp: function(element, cb) {
cb();
},
setTimestamp: function(time, cb) {
cb();
}
};
window.utils.sdcard = MockSdCard;
window.utils.time = {
pretty: function() {}
};
document.body.innerHTML = MockContactsIndexHtml;
contacts.Settings.init();
LazyLoader.load(['/shared/js/contacts/import/utilities/status.js',
'/shared/js/advanced_telemetry_helper.js'], () => {
LazyLoader.load('/contacts/js/utilities/telemetry.js', () => {
realATH = window.AdvancedTelemetryHelper;
window.AdvancedTelemetryHelper = MockAdvancedTelemetryHelper;
done();
});
});
});
suiteTeardown(function() {
navigator.mozMobileConnections = realMozMobileConnections;
navigator.requestWakeLock = realWakeLock;
navigator.mozContacts = realMozContacts;
window.utils = realUtils;
window._ = real_;
window.AdvancedTelemetryHelper = realATH;
mocksHelper.suiteTeardown();
});
setup(function() {
this.sinon.spy(window.utils.status, 'show');
});
test('SD Import went well', function(done) {
sinon.spy(window, 'AdvancedTelemetryHelper');
contacts.Settings.importFromSDCard(function onImported() {
assert.isTrue(window.utils.overlay.showMenu.called);
assert.equal(window.utils.status.show.getCall(0).args.length, 2);
assert.equal(false, MyLocks.cpu);
window.TelemetryReady.then(() => {
assert.isTrue(window.AdvancedTelemetryHelper.calledWithNew());
var counter = window.AdvancedTelemetryHelper.getCall(0).returnValue;
assert.equal(counter.name, 'telemetry_gaia_contacts_import_sd');
assert.equal(window.AdvancedTelemetryHelper.HISTOGRAM_COUNT,
counter.type);
window.AdvancedTelemetryHelper.restore();
done();
});
});
});
test('SD Import went well with duplicates found', function(done) {
MockVCFReader.prototype.numDuplicated = 2;
contacts.Settings.importFromSDCard(function onImported() {
assert.isTrue(window.utils.overlay.showMenu.called);
assert.isTrue(window.utils.status.show.called);
assert.isTrue(window.utils.status.show.getCall(0).args[0] !== null);
assert.isTrue(window.utils.status.show.getCall(0).args[1] !== null);
assert.equal(false, MyLocks.cpu);
delete MockVCFReader.prototype.numDuplicated;
done();
});
});
test('SD Import with error cause no files to import', function(done) {
// Simulate not finding any files
MockSdCard.failOnRetrieveFiles = true;
contacts.Settings.importFromSDCard(function onImported() {
assert.isTrue(window.utils.overlay.showMenu.called);
assert.isFalse(window.utils.status.show.called);
assert.equal(false, MyLocks.cpu);
// Restore the mock
MockSdCard.failOnRetrieveFiles = false;
done();
});
});
suite('SIM Import ', function() {
suiteSetup(function() {
contacts.Settings.init();
});
test('If there are no Contacts to be imported a message appears',
function(done) {
MockSimContactsImporter.prototype.numImportedContacts = 0;
MockSimContactsImporter.prototype.numDuplicated = 0;
MockSimContactsImporter.prototype.number = 0;
contacts.Settings.importFromSIMCard('1234', function onImported() {
assert.isTrue(window.utils.status.show.called);
assert.isTrue(window.utils.status.show.getCall(0).args[0] !== null);
assert.isTrue(window.utils.status.show.getCall(0).args[1] === null);
delete MockSimContactsImporter.prototype.numImportedContacts;
done();
});
});
test('SIM Import went well with duplicates found', function(done) {
MockSimContactsImporter.prototype.numDuplicated = 1;
MockSimContactsImporter.prototype.numImportedContacts = 3;
MockSimContactsImporter.prototype.number = 3;
contacts.Settings.importFromSIMCard('1234', function onImported() {
assert.isTrue(window.utils.status.show.called);
assert.isTrue(window.utils.status.show.getCall(0).args[0] !== null);
assert.isTrue(window.utils.status.show.getCall(0).args[1] !== null);
assert.equal(false, MyLocks.cpu);
delete MockSimContactsImporter.prototype.numDuplicates;
done();
});
});
});
});

View file

@ -0,0 +1,156 @@
'use strict';
/* global SimContactsImporter */
/* global MockMozContacts, MocksHelper, MockIccManager */
require('/shared/test/unit/mocks/mock_mozContacts.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_iccmanager.js');
require('/shared/js/text_normalizer.js');
require('/shared/js/simple_phone_matcher.js');
require('/shared/js/contacts/contacts_merger.js');
require('/shared/js/contacts/utilities/image_thumbnail.js');
require('/shared/js/contacts/merger_adapter.js');
require('/shared/js/contacts/import/utilities/misc.js');
require('/shared/js/contacts/import/utilities/import_sim_contacts.js');
require('/shared/test/unit/mocks/mock_moz_contact.js');
var mocksHelperForImporter = new MocksHelper([
'LazyLoader',
'mozContact'
]).init();
suite('Sim import >', function() {
mocksHelperForImporter.attachTestHelpers();
var realMozContacts,
iccManager,
icc,
importer;
// List of contacts result for the method icc.readContacts
var mRequestResult = {
adn: [
{id : 'a572247', tel : [{value : '900900900'}], name : ['Peter']},
{id : 'b572248', tel : [{value : '192192192'}], name : ['Jason']}
],
sdn: [
{id : '105721', tel : [{value : '190'}], name : ['Police'] },
{id : '105722', tel : [{value : '192'}], name : ['Ambulance'] },
{id : '105723', tel : [{value : '193'}], name : ['Fire'] }
]
};
suiteSetup(function(done) {
realMozContacts = navigator.mozContacts;
navigator.mozContacts = MockMozContacts;
require('/shared/js/contacts/contacts_matcher.js', done);
});
setup(function() {
MockMozContacts.contacts = [];
iccManager = new MockIccManager();
icc = iccManager.getIccById('123');
importer = new SimContactsImporter(icc);
});
suiteTeardown(function() {
navigator.mozContacts = realMozContacts;
});
test('Calling with adn and sdn contacts', function(done) {
iccManager.adnContacts = mRequestResult.adn;
iccManager.sdnContacts = mRequestResult.sdn;
iccManager.isAdnOnError = false;
iccManager.isSdnOnError = false;
var totalContactsToImport = mRequestResult.adn.length +
mRequestResult.sdn.length;
importer.onfinish = function() {
var mozContactsLength = navigator.mozContacts.contacts.length;
assert.equal(mozContactsLength, totalContactsToImport);
done();
};
importer.start();
});
test('Calling without adn contacts', function(done) {
iccManager.adnContacts = [];
iccManager.sdnContacts = mRequestResult.sdn;
iccManager.isAdnOnError = false;
iccManager.isSdnOnError = false;
var totalContactsToImport = mRequestResult.sdn.length;
importer.onfinish = function() {
var mozContactsLength = navigator.mozContacts.contacts.length;
assert.equal(mozContactsLength, totalContactsToImport);
done();
};
importer.start();
});
test('Calling without sdn contacts', function(done) {
iccManager.adnContacts = mRequestResult.adn;
iccManager.sdnContacts = [];
iccManager.isAdnOnError = false;
iccManager.isSdnOnError = false;
var totalContactsToImport = mRequestResult.adn.length;
importer.onfinish = function() {
var mozContactsLength = navigator.mozContacts.contacts.length;
assert.equal(mozContactsLength, totalContactsToImport);
done();
};
importer.start();
});
test('Request adn contacts fails', function(done) {
iccManager.adnContacts = mRequestResult.adn;
iccManager.sdnContacts = mRequestResult.sdn;
iccManager.isAdnOnError = true;
iccManager.isSdnOnError = false;
importer.onerror = function() {
var mozContactsLength = navigator.mozContacts.contacts.length;
assert.equal(mozContactsLength, 0);
done();
};
importer.start();
});
test('Request sdn contacts fails, only import adn', function(done) {
iccManager.adnContacts = mRequestResult.adn;
iccManager.sdnContacts = mRequestResult.sdn;
iccManager.isAdnOnError = false;
iccManager.isSdnOnError = true;
var adnContactsLength = mRequestResult.adn.length;
importer.onfinish = function() {
var mozContactsLength = navigator.mozContacts.contacts.length;
assert.equal(mozContactsLength, adnContactsLength);
done();
};
importer.start();
});
test('Imported Contacts are saved with the iccManager provided id',
function(done) {
iccManager.adnContacts = [mRequestResult.adn[0]];
var realMozContact = window.mozContact;
window.mozContact = function(contact) {
assert.equal(contact.id, mRequestResult.adn[0].id);
};
importer.onfinish = function() {
window.mozContact = realMozContact;
done();
};
importer.start();
});
});

View file

@ -0,0 +1,486 @@
'use strict';
/* global utils, importer, MockAlphaScroll, MockImageLoader */
/* global MockSearch, MockasyncStorage, MockOauthflow, MockImportHtml */
/* global MockConnector, MockImportedContacts, MockCurtain, MockLazyLoader */
/* global MockConfirmDialog */
require('/shared/js/text_normalizer.js');
require('/shared/js/contacts/search.js');
requireApp('communications/contacts/test/unit/mock_l10n.js');
require('/shared/js/contacts/import/importer_ui.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/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_confirm_dialog.js');
requireApp('communications/contacts/test/unit/import/mock_import.html.js');
requireApp('communications/contacts/test/unit/mock_asyncstorage.js');
requireApp('communications/contacts/test/unit/mock_search.js');
requireApp('communications/contacts/test/unit/mock_oauthflow.js');
require('/shared/js/contacts/import/friends_list.js');
requireApp('communications/contacts/test/unit/mock_contacts_shortcuts.js');
requireApp('communications/contacts/test/unit/mock_utils.js');
requireApp('communications/facebook/test/unit/mock_curtain.js');
requireApp('communications/contacts/test/unit/import/mock_connector.js');
requireApp(
'communications/contacts/test/unit/import/mock_imported_contacts.js');
var realSearch,
realImageLoader,
realAlphaScroll,
realAsyncStorage,
realOauthflow,
realCurtain,
realLazyLoader,
realConfirmDialog,
realParentLazyLoader,
groupsListChild, groupsList;
if (!window.asyncStorage) {
window.asyncStorage = null;
}
if (!window.ImageLoader) {
window.ImageLoader = null;
}
if (!window.contacts) {
window.contacts = null;
}
if (!window.onrendered) {
window.onrendered = true;
}
if (!window.oauthflow) {
window.oauthflow = null;
}
if (!window.LazyLoader) {
window.LazyLoader = null;
}
if (!window.ConfirmDialog) {
window.ConfirmDialog = null;
}
setup(function() {
importer.reset();
});
suite('Import Friends Test Suite', function() {
suiteSetup(function() {
realAlphaScroll = utils.AlphaScroll;
utils.alphaScroll = MockAlphaScroll;
realCurtain = window.Curtain;
window.Curtain = MockCurtain;
realImageLoader = window.ImageLoader;
window.ImageLoader = MockImageLoader;
realSearch = window.Search;
window.Search = MockSearch;
realAsyncStorage = window.asyncStorage;
window.asyncStorage = MockasyncStorage;
realOauthflow = window.oauthflow;
window.oauthflow = MockOauthflow;
realLazyLoader = window.LazyLoader;
window.LazyLoader = MockLazyLoader;
realConfirmDialog = window.ConfirmDialog;
window.ConfirmDialog = MockConfirmDialog;
realParentLazyLoader = window.parent.LazyLoader;
window.parent.LazyLoader = MockLazyLoader;
document.body.innerHTML = MockImportHtml;
groupsList = document.body.querySelector('#groups-list');
groupsListChild = groupsList.firstElementChild;
importer.ui.init();
});
test('Import UI. items created. not already present', function(done) {
var contactsLoadedCalled = false;
MockConnector.oncontactsloaded = function() {
contactsLoadedCalled = true;
};
groupsList.innerHTML = '';
groupsList.appendChild(groupsListChild);
importer.start('mock_token', MockConnector, '*', function() {
assert.equal(document.querySelectorAll('#groups-list li').length,
MockImportedContacts.data.length);
// MockAsyncStorage is ordering by first name
assert.isNotNull(document.
querySelector('section#group-P li[data-uuid="1xz"]'));
assert.isNotNull(document.
querySelector('section#group-A li[data-uuid="2abc"]'));
assert.isNotNull(document.
querySelector('section#group-Ψ li[data-uuid="3cde"]'));
assert.equal(document.querySelectorAll('section#group-G *').length, 0);
assert.equal(document.querySelector('input[name="1xz"]').checked, false);
assert.equal(document.querySelector('input[name="2abc"]').checked, false);
assert.equal(document.querySelector('input[name="3cde"]').checked, false);
assert.isTrue(document.getElementById('deselect-all').disabled);
assert.isFalse(document.getElementById('select-all').disabled);
assert.isTrue(document.getElementById('import-action').disabled);
if (contactsLoadedCalled) {
done();
}
else {
assert.fail('contactsLoaded not Called', 'contactsLoadedCalled');
done();
}
});
});
test('Import UI with some contacts already imported', function(done) {
groupsList.innerHTML = '';
groupsList.appendChild(groupsListChild);
var listDeviceContacts = MockConnector.listDeviceContacts;
MockConnector.listDeviceContacts = function(callbacks) {
callbacks.success([
{
uid: '1xz'
}
]);
};
importer.start('mock_token', MockConnector, '*', function() {
var check = document.querySelector(
'li[data-uuid="1xz"] input[type="checkbox"]');
assert.isTrue(check.checked);
var otherCheck = document.querySelector(
'li[data-uuid="2abc"] input[type="checkbox"]');
assert.isFalse(otherCheck.checked);
assert.isFalse(document.getElementById('deselect-all').disabled);
assert.isFalse(document.getElementById('select-all').disabled);
done();
});
MockConnector.listDeviceContacts = listDeviceContacts;
});
test('Import UI with all contacts already imported', function(done) {
groupsList.innerHTML = '';
groupsList.appendChild(groupsListChild);
var listDeviceContacts = MockConnector.listDeviceContacts;
MockConnector.listDeviceContacts = function(callbacks) {
callbacks.success([
{
uid: '1xz'
},
{
uid: '2abc'
},
{
uid: '3cde'
}
]);
};
importer.start('mock_token', MockConnector, '*', function() {
var check = document.querySelector(
'li[data-uuid="1xz"] input[type="checkbox"]');
assert.isTrue(check.checked);
var otherCheck = document.querySelector(
'li[data-uuid="2abc"] input[type="checkbox"]');
assert.isTrue(otherCheck.checked);
var anotherCheck = document.querySelector(
'li[data-uuid="3cde"] input[type="checkbox"]');
assert.isTrue(anotherCheck.checked);
assert.isTrue(document.getElementById('deselect-all').disabled === false);
assert.isTrue(document.getElementById('select-all').disabled === true);
done();
});
MockConnector.listDeviceContacts = listDeviceContacts;
});
test('Import UI, Importing then deleting contacts from Outlook/Gmail ' +
'returns the correct result', function(done) {
groupsList.innerHTML = '';
groupsList.appendChild(groupsListChild);
var listDeviceContacts = MockConnector.listDeviceContacts;
// Simulate contacts already imported from the connector
MockConnector.listDeviceContacts = function(callbacks) {
callbacks.success([
{
uid: '1xz'
},
{
uid: '2abc'
},
{
uid: '3cde'
}
]);
};
// Simulate the desktop deletion of all contacts
this.sinon.stub(MockConnector, 'listAllContacts',
function(access_token, callbacks) {
callbacks.success({data: []});
}
);
var spy = sinon.spy(window.ConfirmDialog, 'show');
importer.start('mock_token', MockConnector, '*', function() {
done(function() {
assert.isTrue(spy.called);
assert.isTrue(spy.calledWith(null, 'emptyAccount'));
});
});
spy.restore();
MockConnector.listDeviceContacts = listDeviceContacts;
});
test('Import UI with no contacts available to import', function(done) {
groupsList.innerHTML = '';
groupsList.appendChild(groupsListChild);
var listAllContacts = MockConnector.listAllContacts;
MockConnector.listAllContacts = function(access_token, callbacks) {
callbacks.success({ data: []});
};
var spy = sinon.spy(window.ConfirmDialog, 'show');
importer.start('mock_token', MockConnector, '*', function() {
done(function() {
assert.isTrue(spy.called);
assert.isTrue(spy.calledWith(null, 'emptyAccount'));
});
});
spy.restore();
MockConnector.listAllContacts = listAllContacts;
});
suite('Import process', function() {
var stubFriendListRenderer;
var listDeviceContacts;
var mockCurtainHide;
var stubImporterImportAll;
var stubParentPostMessage;
suiteSetup(function() {
stubFriendListRenderer = sinon.stub(window.FriendListRenderer, 'render',
function(contacts, cb) {
cb();
});
mockCurtainHide = MockCurtain.hide;
MockCurtain.hide = function (cb) {cb();};
listDeviceContacts = MockConnector.listDeviceContacts;
MockConnector.listDeviceContacts = function(callbacks) {
callbacks.success([{uid: '1xz'}]);
};
stubImporterImportAll = sinon.stub(importer, 'importAll', function(cb) {
cb();
});
});
setup(function() {
groupsList.innerHTML = '';
groupsList.appendChild(groupsListChild);
});
test('Import UI is closed when process ends in FTU', function(done) {
var stubGetContext = sinon.stub(importer, 'getContext', function() {
return 'ftu';
});
stubParentPostMessage = sinon.stub(window.parent, 'postMessage',
function(msg) {
if (msg.type === 'window_close') {
done(function() {
assert.ok('passed');
stubGetContext.restore();
});
}
});
importer.start('mock_token', MockConnector, '*', function() {
importer.setSelected({contact1: null});
importer.ui.importAll();
});
});
test('Import UI is closed when process ends in contacts', function(done) {
stubParentPostMessage = sinon.stub(window.parent, 'postMessage',
function(msg) {
if (msg.type === 'import_updated') {
window.postMessage({type: 'contacts_loaded'}, '*');
} else if (msg.type === 'window_close') {
done(function() {
assert.ok('passed');
});
}
});
importer.start('mock_token', MockConnector, window.location.origin,
function() {
importer.setSelected({contact1: null});
importer.ui.importAll();
});
});
suite('Number of contacts available to import', function() {
var fbContacts;
var deviceContacts;
var observer;
function setObserver(callback) {
observer = new MutationObserver(callback);
var observerTarget = document.getElementById('friends-msg');
observer.observe(observerTarget, {attributes: true});
var MockFbConnector = {
name: 'facebook',
listAllContacts: function(token, cb) {
cb.success({data: fbContacts.map(name => ({ name, uid: name }))});
},
listDeviceContacts: function(cb) {
cb.success(deviceContacts.map(name => ({ name, uid: name })));
},
adaptDataForShowing: function(data) {
return data;
},
getContactUid: function(data) {
return data.uid;
}
};
window.importer.start('test_token', MockFbConnector);
}
suiteSetup(function() {
sinon.stub(window.Curtain, 'hide', function() {});
});
suiteTeardown(function() {
window.Curtain.hide.restore();
});
teardown(function() {
observer.disconnect();
});
test('If fb contacts < device contacts, 0', function(done) {
fbContacts = ['contact1', 'contact2'];
deviceContacts = ['contact1', 'contact2', 'contact3'];
setObserver(function(mutations) {
done(function() {
var l10nAttrs =
navigator.mozL10n.getAttributes(mutations[0].target);
assert.deepEqual(l10nAttrs.args, { numFriends : 0 },
'The number must be 0');
});
});
});
test('If fb contacts = device contacts, 0', function(done) {
fbContacts = ['contact1', 'contact2', 'contact3'];
deviceContacts = ['contact1', 'contact2', 'contact3'];
setObserver(function(mutations) {
done(function() {
var l10nAttrs =
navigator.mozL10n.getAttributes(mutations[0].target);
assert.deepEqual(l10nAttrs.args, { numFriends : 0 },
'The number must be 0');
});
});
});
test('If fb contacts > device contacts, >0', function(done) {
fbContacts = ['contact1', 'contact2', 'contact3'];
deviceContacts = ['contact1', 'contact2'];
setObserver(function(mutations) {
done(function() {
var l10nAttrs =
navigator.mozL10n.getAttributes(mutations[0].target);
assert.deepEqual(l10nAttrs.args, { numFriends : 1 },
'The number must be 1');
});
});
});
});
teardown(function() {
stubParentPostMessage.restore();
});
suiteTeardown(function() {
MockConnector.listDeviceContacts = listDeviceContacts;
stubImporterImportAll.restore();
stubFriendListRenderer.restore();
MockCurtain.hide = mockCurtainHide;
});
});
suiteTeardown(function() {
utils.alphaScroll = realAlphaScroll;
window.ImageLoader = realImageLoader;
window.Search = realSearch;
window.asyncStorage = realAsyncStorage;
window.oauthflow = realOauthflow;
window.curtain = realCurtain;
window.LazyLoader = realLazyLoader;
window.ConfirmDialog = realConfirmDialog;
window.parent.LazyLoader = realParentLazyLoader;
});
});
suite('HTML format', function() {
test('Contact name is tagged with the right class', function() {
function loadImportHTML() {
var req = new XMLHttpRequest();
req.open('GET', '/shared/pages/import/import.html', false);
req.send(null);
return req.responseText;
}
document.body.innerHTML = loadImportHTML();
assert.ok(document.getElementsByClassName('contact-text'), 'Some element' +
'with class contact-text should exist for search highlighting to work');
});
});

View file

@ -0,0 +1,42 @@
'use strict';
/* global MockImportedContacts */
/* exported MockConnector */
var MockConnector = {
listAllContacts: function(access_token, callbacks) {
callbacks.success(MockImportedContacts);
},
listDeviceContacts: function(callbacks) {
callbacks.success([]);
},
getImporter: function(contactsList, access_token) {
},
getCleaner: function(contactsList, access_token) {
},
adaptDataForShowing: function(source) {
return source;
},
adaptDataForSaving: function live2MozContact(liveContact) {
},
getContactUid: function(deviceContact) {
return deviceContact.uid || '-1';
},
get name() {
return 'mock';
},
downloadContactPicture: function(contact, access_token, callbacks) {
callbacks.success();
},
startSync: function() {
}
};

View file

@ -0,0 +1,53 @@
<section role="region" id="content" class="import">
<gaia-header id="link-header" action="close">
<h1 data-l10n-id="fbFriends">Facebook Friends</h1>
<button id="import-action" data-l10n-id="import" disabled>
Import
</button>
</gaia-header>
<section id="main">
<section id="mainContent" data-state="selection">
<form id="search-container" role="search" class="search">
<p>
<label for="search" id="search-start">
<input type="search" name="search" class="textfield" placeholder="Search" data-l10n-id="search-contact">
</label>
</p>
</form>
<section class="friends-msg">
<output id="num-friends" hidden></output>
<p id="friends-msg"></p>
</section>
<section id="groups-list" class="unbordered import-list" data-type="list">
<section id="group-#group#" data-template>
<header>#group#</header>
<ol id="contacts-list-#group#">
<li data-template data-uuid="#uid#" data-search="#search#" aria-disabled="false" class="block-item">
<label class="pack-checkbox">
<input type="checkbox" name="#uid#"></input>
<span></span>
</label>
<aside class="pack-end">
<img data-src="https://graph.facebook.com/#uid#/picture?type=square"></img>
</aside>
<p data-order-by="firstName"><strong>#givenName#</strong> #familyName#</p>
<p data-order-by="lastName">#givenName# <strong>#familyName#</strong></p>
<p>#email1#</p>
</li>
</ol>
</section> <!-- group template content -->
</section> <!-- groupsList -->
<form role="dialog" data-type="confirm" class="no-overlay">
<menu id="select-all-wrapper">
<button id="deselect-all" class="edit-button" data-l10n-id="deselectAll">
Deselect all
</button>
<button id="select-all" class="edit-button" data-l10n-id="selectAll">
Select all
</button>
</menu>
</form>
</section> <!-- mainContent -->

View file

@ -0,0 +1,11 @@
'use strict';
/* exported MockImportHtml */
var MockImportHtml = (function MockGoogleListing() {
var req = new XMLHttpRequest();
req.open('GET', '/contacts/test/unit/import/mock_import.html', false);
req.send(null);
return req.responseText;
})();

View file

@ -0,0 +1,121 @@
'use strict';
/* exported MockImportedContacts */
var MockImportedContacts = {
data: [
{
'uid': '1xz',
'updated': new Date(),
'additionalName': [''],
'adr': [
{
'countryName': 'Germany',
'locality': 'Chemnitz',
'postalCode': '09034',
'streetAddress': 'Gotthardstrasse 22'
}
],
'bday': '1978-12-20',
'email': [
{
'type': 'Personal',
'value': 'test@test.com'
}
],
'familyName': ['Grillo'],
'givenName': ['Pepito'],
'jobTitle': [''],
'name': [
'Pepito Grillo'
],
'org': ['Test'],
'tel': [
{
'value': '+346578888888',
'type': 'Mobile',
'carrier': 'TEF'
}
],
'category': [
'favorite'
],
'note': [
'Note 1'
],
'photo': ['test.png']
}
,
{
'uid': '2abc',
'updated': new Date(),
'additionalName': [''],
'adr': [
{
'countryName': 'Spain',
'locality': 'Burgos',
'postalCode': '09034',
'streetAddress': 'El Espolón, 25'
}
],
'bday': '1932-12-20',
'email': [
{
'type': 'Personal',
'value': 'test2@test2.com'
}
],
'familyName': ['Gonzo'],
'givenName': ['Alfonso'],
'jobTitle': [''],
'name': [
'Alfonso Gonzo'
],
'org': ['Retired 2.0'],
'tel': [
{
'value': '+34612456789',
'type': 'Mobile',
'carrier': 'TEF'
}
],
'photo': ['test2.png']
}
,
{
'uid': '3cde',
'updated': new Date(),
'additionalName': [''],
'adr': [
{
'countryName': 'Spain',
'locality': 'Lugo',
'postalCode': '19034',
'streetAddress': 'Mallorca, 25'
}
],
'bday': '1965-11-20',
'email': [
{
'type': 'Personal',
'value': 'test3@test3.com'
}
],
'familyName': ['Lucca'],
'givenName': ['Ψoca'],
'jobTitle': [''],
'name': [
'Ψoca Lucca'
],
'org': ['Testing'],
'tel': [
{
'value': '+34555666777',
'type': 'Mobile',
'carrier': 'TEF'
}
],
'photo': ['test3.png']
}
]
};

View file

@ -0,0 +1,48 @@
'use strict';
/* exported MockRest */
/*
Mocking the Rest utility class, it's configurable to behave
differently via the configure method
*/
var MockRest = (function MockRest() {
var config = {
// Values are success, error, timeout (the possible callbacks)
'type': 'success'
// Follow with a series of 'urls' and the values returned
};
var configure = function configure(conf) {
config = conf;
};
var get = function get(url, callbacks, options) {
if (!config[url]) {
callbacks.error('No matching for url ' + url);
return;
}
switch (config.type) {
case 'success':
callbacks.success(config[url]);
break;
case 'error':
callbacks.error(config[url]);
break;
case 'timeout':
callbacks.timeout(config[url]);
break;
default:
callbacks.timeout(config[url]);
break;
}
};
return {
'get': get,
'configure': configure
};
})();

View file

@ -0,0 +1,80 @@
'use strict';
/* global Redirect */
require('/shared/pages/import/redirects/redirect.js');
suite('OAuth redirect', function() {
var openerDefinition;
var myOpenerObject = {
postMessage: function() {}
};
suiteSetup(function() {
openerDefinition = Object.getOwnPropertyDescriptor(window, 'opener');
Object.defineProperty(window, 'opener', {
writable: true,
enumerable: true,
configurable: true,
value: myOpenerObject
});
sinon.stub(window, 'close', function() {
});
});
setup(function() {
window.close.reset();
});
suiteTeardown(function() {
Object.defineProperty(window, 'opener', openerDefinition);
window.close.restore();
});
suite('> Recirect with valid access token', function() {
test('> Check token', function() {
this.sinon.spy(window.opener, 'postMessage');
document.location.hash = 'access_token=ok';
Redirect.init();
sinon.assert.pass(window.close.calledOnce);
sinon.assert.pass(myOpenerObject.postMessage.calledOnce);
sinon.assert.calledWith(myOpenerObject.postMessage,
{'access_token': 'ok'});
});
test('> Check parameters', function() {
this.sinon.spy(window.opener, 'postMessage');
document.location.hash = 'access_token=ok&param1=1&param2=2';
Redirect.init();
sinon.assert.pass(window.close.calledOnce);
sinon.assert.pass(myOpenerObject.postMessage.calledOnce);
sinon.assert.calledWith(myOpenerObject.postMessage,
{
'access_token': 'ok',
'param1': '1',
'param2': '2'
}
);
});
});
suite('> Redirect with invalid access token', function() {
test('No access token', function() {
this.sinon.spy(window.opener, 'postMessage');
document.location.hash = 'invalid';
Redirect.init();
sinon.assert.pass(window.close.calledOnce);
sinon.assert.callCount(myOpenerObject.postMessage, 0);
});
});
});

View file

@ -0,0 +1,199 @@
'use strict';
/* global MocksHelper, MockLoader,
MockMozL10n, utils, MockMatcher, MockMozContacts */
require('/shared/js/contacts/import/utilities/import_from_vcard.js');
require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_moz_contact.js');
require('/shared/test/unit/mocks/mock_mozContacts.js');
requireApp('communications/contacts/test/unit/mock_contacts_match.js');
requireApp('communications/contacts/test/unit/mock_l10n.js');
requireApp('communications/contacts/test/unit/mock_vcard_reader.js');
requireApp('communications/contacts/test/unit/mock_file_reader.js');
require('/shared/test/unit/mocks/mock_confirm_dialog.js');
requireApp('communications/contacts/test/unit/mock_navigation.js');
requireApp('communications/contacts/test/unit/mock_contacts.js');
requireApp('communications/contacts/test/unit/mock_loader.js');
requireApp('/shared/test/unit/mocks/mock_moz_contact.js');
if (!window._) {
window._ = null;
}
if (!window.utils) {
window.utils = null;
}
var mocksHelperForImportVcard = new MocksHelper([
'LazyLoader',
'mozContact',
'Contacts',
'VCardReader',
'ConfirmDialog',
'FileReader'
]).init();
var vcardSingle = 'BEGIN:VCARD\n' +
'VERSION:2.1\n' +
'N;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:Gump;F=C3=B3rrest\n' +
'ORG;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:B=C3=B3bba Gump Shrimp Co.\n' +
'TITLE;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:Shr=C3=B3mp Man\n' +
'PHOTO;GIF:http://www.example.com/dir_photos/my_photo.gif\n' +
'TEL;WORK;VOICE:(111) 555-1212\n' +
'TEL;HOME;VOICE:(404) 555-1212\n' +
'ADR;WORK;ENCODING=QUOTED-PRINTABLE:;;100 W=C3=A1ters Edge;Baytown;LA;' +
'30314;United States of America\n' +
'LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, ' +
'LA 30314=0D=0AUnited States of America\n' +
'ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America\n' +
'LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0ABaytown, ' +
'LA 30314=0D=0AUnited States of America\n' +
'EMAIL;PREF;INTERNET:forrestgump@example.com\n' +
'REV:20080424T195243Z\n' +
'END:VCARD';
var vcardMultiple = 'BEGIN:VCARD\n' +
'VERSION:2.1\n' +
'N;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:Gump;F=C3=B3rrest\n' +
'ORG;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:B=C3=B3bba Gump Shrimp Co.\n' +
'TITLE;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:Shr=C3=B3mp Man\n' +
'PHOTO;GIF:http://www.example.com/dir_photos/my_photo.gif\n' +
'TEL;WORK;VOICE:(111) 555-1212\n' +
'TEL;HOME;VOICE:(404) 555-1212\n' +
'ADR;WORK;ENCODING=QUOTED-PRINTABLE:;;100 W=C3=A1ters Edge;Baytown;LA;' +
'30314;United States of America\n' +
'LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, ' +
'LA 30314=0D=0AUnited States of America\n' +
'ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America\n' +
'LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0ABaytown, ' +
'LA 30314=0D=0AUnited States of America\n' +
'EMAIL;PREF;INTERNET:forrestgump@example.com\n' +
'REV:20080424T195243Z\n' +
'END:VCARD' +
'BEGIN:VCARD\n' +
'VERSION:2.1\n' +
'N;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:Gump;F=C3=B3rrest\n' +
'ORG;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:B=C3=B3bba Gump Shrimp Co.\n' +
'TITLE;ENCODING=QUOTED-PRINTABLE;CHARSET=utf-8:Shr=C3=B3mp Man\n' +
'PHOTO;GIF:http://www.example.com/dir_photos/my_photo.gif\n' +
'TEL;WORK;VOICE:(111) 555-1212\n' +
'TEL;HOME;VOICE:(404) 555-1212\n' +
'ADR;WORK;ENCODING=QUOTED-PRINTABLE:;;100 W=C3=A1ters Edge;Baytown;LA;' +
'30314;United States of America\n' +
'LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, ' +
'LA 30314=0D=0AUnited States of America\n' +
'ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America\n' +
'LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0ABaytown, ' +
'LA 30314=0D=0AUnited States of America\n' +
'EMAIL;PREF;INTERNET:forrestgump@example.com\n' +
'REV:20080424T195243Z\n' +
'END:VCARD';
var vcardError = 'error';
var contact1 = {
id: '0'
};
suite('Import from vcard', function() {
var realMozL10n,
real_,
realStatus,
realMatcher,
realOverlay,
realLoader;
suiteSetup(function() {
realMozL10n = navigator.mozL10n;
navigator.mozL10n = MockMozL10n;
real_ = window._;
window._ = navigator.mozL10n.get;
realStatus = window.utils.status;
realOverlay = window.utils.overlay;
navigator.mozContacts = MockMozContacts;
sinon.stub(navigator.mozContacts, 'find', function mockMozContactsFind() {
var self = this;
var req = {
set onsuccess(cb) {
req.result = self.contacts;
cb();
},
set onerror(cb) {},
};
return req;
});
window.contacts = window.contacts || {};
realMatcher = window.Matcher;
window.Matcher = MockMatcher;
realLoader = window.Loader;
window.Loader = MockLoader;
window.utils.overlay = {
total: 0,
show: function() {
return this;
},
hide: function() {},
showMenu: function() {},
update: function() {},
setClass: function() {},
setTotal: function(n) {
this.total = n;
},
setHeaderMsg: function() {}
};
window.utils.status = {
show: function() {}
};
mocksHelperForImportVcard.suiteSetup();
});
setup(function() {
window.utils.overlay.total = 0;
navigator.mozContacts.contacts = [];
mocksHelperForImportVcard.setup();
});
suiteTeardown(function() {
navigator.mozL10n = realMozL10n;
window._ = real_;
window.Matcher = realMatcher;
window.utils.status = realStatus;
window.utils.overlay = realOverlay;
window.Loader = realLoader;
mocksHelperForImportVcard.suiteTeardown();
});
test('Import single contact', function(done) {
utils.importFromVcard(vcardSingle, function(numberOfContacts, id) {
assert.equal(id, contact1.id, 'return id of the contact imported');
assert.equal(window.utils.overlay.total, 1);
done();
});
});
test('Import multiple contacts', function(done) {
utils.importFromVcard(vcardMultiple, function(numberOfContacts, id) {
assert.equal(id, contact1.id, 'returns id of the first contact imported');
assert.equal(window.utils.overlay.total, 2);
done();
});
});
test('Error while importing', function(done) {
utils.importFromVcard(vcardError, function(numberOfContacts, id) {
assert.isUndefined(id, 'returns no id as there was an error');
assert.equal(window.utils.overlay.total, 0);
done();
});
});
});

View file

@ -0,0 +1,259 @@
'use strict';
/* global fb */
/* global MockAllFacebookContacts:true */
/* global MockasyncStorage */
/* global Mockfb */
/* global MockImageLoader */
/* global MockLinkedContacts:true */
/* global MockLinkHtml */
/* global MockOauthflow */
/* global MockCurtain */
require('/shared/js/text_normalizer.js');
require('/shared/js/binary_search.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_link.html.js');
requireApp('communications/contacts/test/unit/mock_l10n.js');
requireApp('communications/facebook/test/unit/mock_curtain.js');
requireApp('communications/contacts/test/unit/mock_utils.js');
requireApp('communications/contacts/test/unit/mock_asyncstorage.js');
requireApp('communications/contacts/test/unit/mock_linked_contacts.js');
requireApp('communications/contacts/test/unit/mock_fb.js');
requireApp('communications/contacts/test/unit/mock_oauthflow.js');
requireApp('communications/contacts/js/fb/fb_link.js');
var realImageLoader,
realAsyncStorage,
realFb,
realOauthflow,
realCurtain,
linkProposal,
linkProposalChild;
if (!window.asyncStorage) {
window.asyncStorage = null;
}
if (!window.ImageLoader) {
window.ImageLoader = null;
}
if (!window.fb) {
window.fb = null;
}
if (!window.oauthflow) {
window.oauthflow = null;
}
suite('Link Friends Test Suite', function() {
var spy;
suiteSetup(function() {
realImageLoader = window.ImageLoader;
window.ImageLoader = MockImageLoader;
realCurtain = window.Curtain;
window.Curtain = MockCurtain;
realAsyncStorage = window.asyncStorage;
window.asyncStorage = MockasyncStorage;
realFb = window.fb;
window.fb = Mockfb;
window.fb.link = realFb.link;
realOauthflow = window.oauthflow;
window.oauthflow = MockOauthflow;
spy = sinon.spy(fb.utils, 'setCachedNumFriends').withArgs(
MockAllFacebookContacts.data.length);
document.body.innerHTML = MockLinkHtml;
linkProposal = document.body.querySelector('#friends-list');
linkProposalChild = linkProposal.firstElementChild;
fb.link.init();
});
teardown(function() {
spy.reset();
});
test('Link UI. Proposal Calculated', function(done) {
linkProposal.innerHTML = '';
linkProposal.appendChild(linkProposalChild);
fb.utils.result = {
givenName: ['Jose'],
tel: [{
type: ['home'],
value: '678956345'
}]
};
fb.link.start('mock_token', '123456', function() {
// As the template itself also counts
assert.equal(document.querySelectorAll('#friends-list li').length, 3);
// Here we check for the UIDs of the MockLinkedContacts
// which are defined on the mock_linked_contacts.js source
assert.isNotNull(document.
querySelector('li[data-uuid="1xz"]'));
assert.isNotNull(document.
querySelector('li[data-uuid="2abc"]'));
// Check that the total number of friends is properly cached (bug 838605)
assert.isTrue(spy.calledOnce);
done();
});
});
test('Link UI. No proposals. Showing All', function(done) {
linkProposal.innerHTML = '';
linkProposal.appendChild(linkProposalChild);
var oldMockLinkedContacts = MockLinkedContacts;
MockLinkedContacts = {
data: []
};
fb.link.start('mock_token', '123456', function() {
// As the template itself also counts
assert.equal(document.querySelectorAll('#friends-list li').length, 5);
// Here we check for the UIDs of the MockAllFacebookContacts
// which are defined on the mock_linked_contacts.js source
assert.isNotNull(document.
querySelector('li[data-uuid="5678x"]'));
assert.isNotNull(document.
querySelector('li[data-uuid="56zwt"]'));
assert.isNotNull(document.
querySelector('li[data-uuid="kjh2389"]'));
assert.isNotNull(document.
querySelector('li[data-uuid="aa45bb"]'));
MockLinkedContacts = oldMockLinkedContacts;
// Check that the total number of friends is properly cached (bug 838605)
assert.isTrue(spy.calledOnce);
done();
});
});
test('Link UI. Matching by givenName >accents in proposal', function(done) {
linkProposal.innerHTML = '';
linkProposal.appendChild(linkProposalChild);
fb.utils.result = {
givenName: ['Angela']
};
fb.link.start('mock_token', '123456', function() {
// As the template itself also counts
assert.equal(document.querySelectorAll('#friends-list li').length, 2);
// Here we check for the id of the MockLinkedContact whose first_name
// is 'Ángela'
assert.isNotNull(document.
querySelector('li[data-uuid="kjh2389"]'));
done();
});
});
test('Link UI. Matching by givenName >accents in local data', function(done) {
linkProposal.innerHTML = '';
linkProposal.appendChild(linkProposalChild);
var oldMockAllFacebookContacts = MockAllFacebookContacts;
MockAllFacebookContacts.data[2].name = 'Angela Cuts';
MockAllFacebookContacts.data[2].first_name = 'Angela';
// To enable the accents match
fb.utils.result = {
givenName: ['Ángela']
};
fb.link.start('mock_token', '123456', function() {
// As the template itself also counts
assert.equal(document.querySelectorAll('#friends-list li').length, 2);
// Here we check for the id of the MockLinkedContact whose first_name
// is 'Angela'
assert.isNotNull(document.
querySelector('li[data-uuid="kjh2389"]'));
MockAllFacebookContacts = oldMockAllFacebookContacts;
done();
});
});
test('Link UI. Matching by familyName > accents in both', function(done) {
linkProposal.innerHTML = '';
linkProposal.appendChild(linkProposalChild);
fb.utils.result = {
familyName: ['González']
};
fb.link.start('mock_token', '123456', function() {
// As the template itself also counts
assert.equal(document.querySelectorAll('#friends-list li').length, 2);
// Here we check for the id of the MockLinkedContact whose lasr_name
// is 'González'
assert.isNotNull(document.
querySelector('li[data-uuid="aa45bb"]'));
done();
});
});
suite('Link UI process', function() {
test('Link UI process ends as expected', function(done) {
linkProposal.innerHTML = '';
linkProposal.appendChild(linkProposalChild);
sinon.stub(window.Curtain, 'hide', function(callback) {
if (typeof callback === 'function') {
callback();
}
});
sinon.stub(window.parent, 'postMessage', function(msg) {
done(function() {
window.Curtain.hide.restore();
window.parent.postMessage.restore();
assert.equal(msg.type, 'ready', 'Sent message is of type \'ready\'.');
});
});
fb.link.start('mock_token', '123456');
});
});
suiteTeardown(function() {
window.ImageLoader = realImageLoader;
window.asyncStorage = realAsyncStorage;
window.fb = realFb;
window.oauthflow = realOauthflow;
window.Curtain = realCurtain;
});
});

View file

@ -0,0 +1,146 @@
'use strict';
/* global Migrator, MockImportStatusData, Mockfb, MockDatastoreMigration,
MockasyncStorage */
requireApp('communications/contacts/test/unit/mock_import_status_data.js');
requireApp('communications/contacts/test/unit/mock_asyncstorage.js');
requireApp('communications/contacts/test/unit/mock_fb.js');
requireApp('communications/contacts/test/unit/mock_datastore_migrator.js');
requireApp('communications/contacts/js/migrator.js');
if (!window.LazyLoader) {
var LazyLoader = {load: function(){}};
}
if (!window.ImportStatusData) {
window.ImportStatusData = null;
}
if (!window.fb) {
window.fb = null;
}
if (!window.DatastoreMigration) {
window.DatastoreMigration = null;
}
if (!window.asyncStorage) {
window.asyncStorage = null;
}
if (!window.utils) {
window.utils = {
cookie: {update: function() {}}
};
}
suite('Migrator module', function() {
var realImportStatusData,
realFb,
realDatastoreMigration,
realAsyncStorage;
suiteSetup(function() {
realImportStatusData = window.ImportStatusData;
window.ImportStatusData = MockImportStatusData;
realFb = window.fb;
window.fb = Mockfb;
realDatastoreMigration = window.DatastoreMigration;
window.DatastoreMigration = MockDatastoreMigration;
realAsyncStorage = window.asyncStorage;
window.asyncStorage = MockasyncStorage;
});
suiteTeardown(function() {
window.ImportStatusData = realImportStatusData;
window.fb = realFb;
window.DatastoreMigration = realDatastoreMigration;
window.asyncStorage = realAsyncStorage;
});
setup(function() {
MockasyncStorage.clear();
MockImportStatusData.clear();
});
test('Don\'t migrate anything', function() {
var spy = sinon.spy(LazyLoader, 'load');
Migrator.start({
fbMigrated: true,
accessTokenMigrated: true
});
assert.isFalse(spy.called, 'A migration was not triggered.');
LazyLoader.load.restore();
});
test('Trigger facebook contacts migration only', function(done) {
sinon.stub(LazyLoader, 'load', function(files, callback) {
callback();
});
window.DatastoreMigration = function() {
return {
start: function() {
window.DatastoreMigration = MockDatastoreMigration;
LazyLoader.load.restore();
done();
}
};
};
Migrator.start({
fbMigrated: false,
accessTokenMigrated: true
});
});
test('Trigger facebook token migration only', function(done) {
sinon.stub(LazyLoader, 'load', function(files, callback) {
callback();
});
window.fb.utils = {
get TOKEN_DATA_KEY() {
window.fb.utils = {TOKEN_DATA_KEY: null};
window.fb = Mockfb;
LazyLoader.load.restore();
done();
}
};
Migrator.start({
fbMigrated: true,
accessTokenMigrated: false
});
});
test('Don\'t migrate fb token if it is not in asyncStorage', function() {
MockasyncStorage.keys[Mockfb.utils.TOKEN_DATA_KEY] = true;
sinon.stub(LazyLoader, 'load', function(files, callback) {
callback();
});
var spy = sinon.spy(window.utils.cookie, 'update');
sinon.stub(window.asyncStorage, 'removeItem', function(key) {
window.asyncStorage.removeItem.restore();
LazyLoader.load.restore();
assert.isTrue(spy.called);
spy.restore();
assert.equal(key, Mockfb.utils.TOKEN_DATA_KEY);
});
Migrator.start({
fbMigrated: true,
accessTokenMigrated: false
});
});
});

View file

@ -0,0 +1,22 @@
'use strict';
/* exported ActionMenu */
function ActionMenu() {
return {
data: {
list: []
},
show: function() {
this.data.list[0].callback();
},
hide: function() {
this.data.list = [];
},
addToList: function(labelL10n, callback) {
this.data.list.push({
'type': [labelL10n],
'callback': callback
});
}
};
}

View file

@ -0,0 +1,23 @@
/* exported MockActivityHandler */
'use strict';
var MockActivityHandler = {
currentlyHandling: false,
activityName: 'view',
currentActivityIs: function(list) {
return this.currentlyHandling && list.indexOf(this.activityName) !== -1;
},
currentActivityIsNot: function(list) {
return this.currentlyHandling && list.indexOf(this.activityName) === -1;
},
postPickSuccess: function(data) {},
postCancel: function() {},
dataPickHandler: function() {},
isCancelable: function() {
return {
then: function(cb) {
cb();
}
};
}
};

View file

@ -0,0 +1,29 @@
'use strict';
/* exported MockasyncStorage */
var MockasyncStorage = {
keys: {},
getItem: function(key, cb) {
if (!cb) {
return;
}
cb(this.keys[key]);
},
setItem: function(key, value, cb) {
this.keys[key] = value;
if (typeof cb === 'function') {
cb();
}
},
removeItem: function(key) {
delete this.keys[key];
},
clear: function(key) {
this.keys = {};
}
};

View file

@ -0,0 +1,38 @@
'use strict';
/* exported MockCache */
var MockCache = {
active: false,
enabled: false,
setFirstChunk(content) {
},
hasContact: function(uuid) {
},
hasFavorite: function(uuid) {
},
getContact: function(uuid) {
},
getFavorite: function(uuid) {
},
get headers() {
return {};
},
verify: function() {
},
cleanup: function() {
},
evict: function() {
},
maybeEvict: function() {
}
};

View file

@ -0,0 +1,33 @@
'use strict';
/* exported MockConfirmDialog */
var MockConfirmDialog = {
showing: false,
title: null,
text: null,
noObject: null,
yesObject: null,
show: function(title, text, no, yes) {
this.showing = true;
this.title = title;
this.text = text;
this.noObject = no;
this.yesObject = yes;
},
hide: function() {
this.showing = false;
this.title = null;
this.text = null;
this.button = null;
},
executeNo: function() {
if (this.noObject && this.noObject.callback) {
this.noObject.callback();
}
},
executeYes: function() {
if (this.yesObject && this.yesObject.callback) {
this.yesObject.callback();
}
}
};

View file

@ -0,0 +1,50 @@
var ContactListDom =
' <!-- Contacts List Section -->' +
' <section id="view-contacts-list" role="region" data-state="active" class="view view-noscroll view-contacts-list">' +
' <gaia-header id="activity-header" action="close">' +
' <h1 id="app-title" data-l10n-id="contacts">Contacts</h1>' +
' <button id="add-contact-button" data-icon="add" data-l10n-id="addButton"></button>' +
' <button id="settings-button" data-icon="settings" data-l10n-id="settingsButton"></button>' +
' </gaia-header>' +
' <article class="view-body">' +
' <div id="no-contacts" class="hide">' +
' <div class="arrow small">' +
' <p class="small" data-l10n-id="create-new">Create a new contact</p>' +
' </div>' +
' <div class="arrow big">' +
' <p class="small" data-l10n-id="import-now">Import your contacts from your SIM card or from Facebook</p>' +
' </div>' +
' <div class="info">' +
' <p data-l10n-id="no-contacts">no contacts</p>' +
' <p data-l10n-id="start-adding">start adding them now </p>' +
' </div>' +
' </div>' +
' <nav data-type="scrollbar">' +
' <p></p>' +
' <ol>' +
' <li data-anchor="search-container" data-img="magnifier_icon_big.png">' +
' <a href="#"><span class="pack-icon-search">Search</span></a>' +
' </li>' +
' <li data-anchor="group-favorites" data-img="star_icon_big.png">' +
' <a href="#"><span class="pack-icon-favorites">Favorites</span></a>' +
' </li>' +
' <li data-template data-anchor="group-#anchor#" data-letter="#anchor#">' +
' <a href="#">#anchor#</a>' +
' </li>' +
' </ol>' +
' </nav>' +
' <div id="groups-container" class="view-body-inner">' +
' <form id="search-container" class="search" role="search">' +
' <p>' +
' <label for="search" id="search-start">' +
' <input type="search" name="search" class="textfield" placeholder="Search" data-l10n-id="search-contact">' +
' </label>' +
' </p>' +
' </form>' +
' <section id="groups-list" data-type="list">' +
' <!-- Group template here -->' +
' </section>' +
' </div>' +
' </article>' +
' </section>';

View file

@ -0,0 +1,51 @@
'use strict';
/* exported MockContacts */
/* exported COMMS_APP_ORIGIN */
var COMMS_APP_ORIGIN = location.origin;
var loadAsyncScriptsDeferred = {};
loadAsyncScriptsDeferred.promise = new Promise((resolve) => {
loadAsyncScriptsDeferred.resolve = resolve;
});
var MockContacts = {
isEmpty: function(prop) {
return false;
},
hideOverlay: function() {
},
loadFacebook: function(cb) {
cb();
},
cardStateChanged: function() {
},
cancel: function() {},
utility: function(view, callback, type) {
callback();
},
view: function(view, callback) {
callback();
},
showOverlay: function(title, id) {
return {
'setClass': function(clazz) {},
'setHeaderMsg': function(msg) {},
'setTotal': function(total) {},
'update': function() {}
};
},
goBack: function(cb) {
if (typeof cb === 'function') {
cb();
}
},
setCurrent: function(ct) {
},
get asyncScriptsLoaded() {
loadAsyncScriptsDeferred.resolve();
return loadAsyncScriptsDeferred.promise;
},
showForm: function() {}
};

View file

@ -0,0 +1,8 @@
'use strict';
/* exported MockAdaptAndMerge */
var MockAdaptAndMerge = function (contact, matches, callbacks) {
callbacks.error();
};

View file

@ -0,0 +1,47 @@
'use strict';
var MockContactsButtonsDom = `<section id="contact-detail">
<section id="phone-details">
<li data-phone id='phone-details-template-#i#' data-template>
<h2>
<span data-l10n-id="#type_l10n_id#">#type#</span>
<span class="carrier-wrapper" hidden>
<span data-l10n-id="separator"></span>
<span class="carrier">#carrier#</span>
</span>
</h2>
<div class="item">
<div class="item-media pull-right">
<button id="send-sms-button-#i#"
class="activity send-sms icon icon-message"
data-l10n-id="send-sms-button" aria-label="SMS"></button>
</div>
<div class="item-body-exp">
<button class="activity call-action icon icon-call"
id="call-or-pick-#i#">
<b>#value#</b>
<span class="js-sim-indication"></span>
</button>
</div>
</div>
</li>
</section>
<section id="email-details">
<li data-mail id='email-details-template-#i#' data-template>
<h2 data-l10n-id="#type_l10n_id#">#type#</h2>
<div class="item">
<div class="item-body-exp">
<button id="email-or-pick-#i#" class="activity icon icon-email">
<b>#value#</b>
</button>
</div>
<div class="item-media pull-right">
<button id="send-sms-to-email-button-#i#"
class="activity send-sms icon icon-message"
data-l10n-id="send-sms-button" aria-label="SMS"></button>
</div>
</div>
</li>
</section>
<ul id="list-details"></ul>
</section>`;

View file

@ -0,0 +1,8 @@
'use strict';
/* exported MockContactDetails */
var MockContactDetails = {
'init': function init() {},
'render': function render() {},
'setContact': function setContact(c) {}
};

View file

@ -0,0 +1,10 @@
'use strict';
/* exported MockContactsExporter */
var MockContactsExporter = function() {
return {
'init': function(ids, cb) {},
'setExportStrategy': function(st) {},
'start': function() {}
};
}();

View file

@ -0,0 +1,6 @@
'use strict';
/* exported MockContactsForm */
var MockContactsForm = {
'init': function init() {}
};

View file

@ -0,0 +1,549 @@
<meta name="defaultLanguage" content="en-US">
<meta name="availableLanguages" content="en-US">
<link rel="localization" href="locales/contacts.{locale}.properties">
<link rel="localization" href="../dialer/locales/shared.{locale}.properties">
<link rel="localization" href="/shared/locales/import_contacts/import_contacts.{locale}.properties">
<!-- Contacts List Section -->
<section id='view-contacts-list' role="region" data-state="active" class="view view-noscroll view-contacts-list">
<gaia-header id="activity-header" action="close">
<h1 id="app-title" data-l10n-id="contacts">Contacts</h1>
<button id="add-contact-button" data-icon="add" data-l10n-id="addButton"></button>
<button id="settings-button" data-icon="settings" data-l10n-id="settingsButton"></button>
</gaia-header>
<article class="view-body">
<div id="no-contacts" class="hide">
<div class="info">
<p data-l10n-id="no-contacts">no contacts</p>
<p data-l10n-id="start-adding">start adding them now </p>
<div class="icon create">
<p class="small" data-l10n-id="create-new">Create a new contact</p>
</div>
<div class="icon import">
<p class="small" data-l10n-id="import-now">Import your contacts from your SIM card or from Facebook</p>
</div>
</div>
</div>
<nav data-type="scrollbar">
<p></p>
<ol>
<li data-anchor="search-container" data-img="magnifier_icon_big.png">
<a href="#"><span class="pack-icon-search">Search</span></a>
</li>
<li data-anchor="group-favorites" data-img="star_icon_big.png">
<a href="#"><span class="pack-icon-favorites">Favorites</span></a>
</li>
<li data-template data-anchor="group-#anchor#" data-letter="#anchor#">
<a href="#">#anchor#</a>
</li>
</ol>
</nav>
<div id="groups-container" class="view-body-inner">
<form id="search-container" class="search" role="search">
<p>
<label for="search" id="search-start">
<input type="search" name="search" class="textfield" placeholder="Search" data-l10n-id="search-contact">
</label>
</p>
</form>
<section id="groups-list" data-type="list">
<!-- Group template here -->
</section>
</div>
</article>
</section>
<section id="search-view" class="view">
<form id="searchview-container" class="search" role="search">
<button id='cancel-search' data-l10n-id="cancel" type="submit">Cancel</button>
<p>
<label for="search-contact">
<input type="search" name="search" class="textfield" placeholder="Search"
id="search-contact" data-l10n-id="search-contact">
<button type="reset">Clear</button>
</label>
</p>
</form>
<section id="groups-list-search">
<ol id="search-list" data-type="list">
</ol>
</section>
<section>
<p id="no-result" class="hide" data-l10n-id="noResults">No contacts found</p>
<p id="search-progress" class="hidden" role="status">
<progress class="small"></progress>
</p>
</section>
</section> <!-- Search view -->
<section id='view-screenshot' role="region" class="view hide">
<gaia-header id="activity-header" action="close">
<h1 id="app-title" data-l10n-id="contacts">Contacts</h1>
<button id="add-contact-button" data-icon="add" data-l10n-id="addButton"></button>
<button id="settings-button" data-icon="settings" data-l10n-id="settingsButton"></button>
</gaia-header>
<article id="view-screenshot-body">
</article>
</section>
<!-- Contact Details Section -->
<section id='view-contact-details' role="region" class="view view-right view-contact-profile">
<gaia-header id='details-view-header' action="back">
<h1 id='contact-name-title'><bdi></bdi></h1>
<button id="edit-contact-button" data-icon="edit-contact" data-l10n-id="editButton"></button>
</gaia-header>
<article id="contact-detail-wrapper" class="view-body">
<div id="cover-img"></div>
<section id="contact-detail" class="view-body-inner">
<!-- Phone Template -->
<li data-phone id='phone-details-template-#i#' data-template>
<h2 data-l10n-id="#type_l10n_id#">#type#</h2>
<div class="item">
<div class="item-media pull-right">
<button id="send-sms-button-#i#" class="activity send-sms icon icon-message"></button>
</div>
<div class="item-body-exp">
<button class="activity icon icon-call" id="call-or-pick-#i#">
<b>
#value#
<em>#carrier#</em>
</b>
</button>
</div>
</div>
</li>
<li id="birthday-template-#i#" data-template>
<h2 data-l10n-id="birthday">Birthday</h2>
<p><strong>#bday#</strong></p>
</li>
<!-- Email Template -->
<li data-mail id='email-details-template-#i#' data-template>
<h2 data-l10n-id="#type_l10n_id#">#type#</h2>
<div class="item">
<div class="item-body-exp">
<button id="email-or-pick-#i#" class="activity icon icon-email">
<b>#value#</b>
</button>
</div>
</div>
</li>
<!-- Social Template -->
<li data-social class="social-actions" id="social-template-#i#" data-template>
<h2 data-l10n-id="social-network" id="social-label">Social Network</h2>
<div class="fillflow-twocols">
<button id="msg_button" data-l10n-id="msg-send" class="activity icon icon-message">
Message
</button>
<button id="wall_button" data-l10n-id="wall-post" class="activity icon icon-email">
Wall Post
</button>
</div>
<button id="profile_button" data-l10n-id="show-fb-profile" class="fillflow-row activity icon icon-fb-profile">
View Facebook profile
</button>
<button id="link_button" class="activity icon icon-link">
#action#
</button>
</li>
<!-- Address Template -->
<li data-address id='address-details-template-#i#' data-template>
<h2 data-l10n-id="#type_l10n_id#">#type#</h2>
<div class="item">
<div class="item-body-exp">
<a href="#" class="action action-block">
<span role="button" class="icon-address bb-button" data-l10n-id="legendAddress">Address</span>
<b>
<span>#streetAddress#</span>
#postalCode#
#locality#
#countryName#
</b>
</a>
</div>
</div>
</li>
<!-- Comment Template -->
<p data-comment id='note-details-template-#i#' data-template class="comment-notes">#note#</p>
<div id='contact-detail-inner'>
<h1 id='org-title'></h1>
<ul id='details-list'>
<!-- Iterating mails, phones, addresses adn comments here -->
</ul>
<ul>
<li>
<button id='toggle-favorite' class="fillflow-row icon icon-fav">
Add as favorite
</button>
</li>
</ul>
</div>
</section>
</article>
</section>
<!-- Edit Section -->
<section id='view-contact-form' role="region" class="view view-bottom view-edit-contact">
<gaia-header id="contact-form-header" action="close">
<h1 id='contact-form-title' data-l10n-id="addContact">Add contact</h1>
<button id="save-button" data-l10n-id="done">Done</button>
</gaia-header>
<!-- PHONE TEMPLATE -->
<div id='add-phone-#i#' data-template class='phone-template' data-index="#i#">
<fieldset class="fillflow-row">
<legend class="action">
<span data-taglist="phone-type" name="tel" data-value="#type_value#" id="tel_type_#i#" data-field="type" data-l10n-id=#type_l10n_id#>#type#</span>
</legend>
<section>
<p class="setbox-item">
<input placeholder="Phone" data-field="value" data-l10n-id="tel" name="tel[#i#][value]" class="textfield" type="tel" value="#value#" id="number_#i#">
<button type="reset">Clear</button>
</p>
<p class="setbox-item">
<input placeholder="Carrier" data-field="carrier" data-l10n-id="carrier" name="tel[#i#][carrier]" class="textfield" type="text" x-inputmode="latin-prose" value="#carrier#" id="carrier_#i#">
<button type="reset">Clear</button>
</p>
</section>
</fieldset>
</div>
<!-- EMAIL TEMPLATE -->
<div id='add-email-#i#' data-template class='email-template' data-index="#i#">
<fieldset class="fillflow-row">
<legend class="action">
<span data-taglist="email-type" name="email[#i#][type]" data-value="#type_value#" id="email_type_#i#" data-field="type" data-l10n-id="#type_l10n_id#">#type#</span>
</legend>
<section>
<p class="setbox-item">
<input placeholder="Email" data-l10n-id="email" name="email[#i#][value]" class="textfield" type="email" value="#value#" id="email_#i#">
<button type="reset">Clear</button>
</p>
</section>
</fieldset>
</div>
<!-- ADDRESS TEMPLATE -->
<div id='add-address-#i#' data-template class='address-template' data-index="#i#">
<fieldset class="fillflow-row">
<legend class="action">
<span data-taglist="address-type" name="adr[#i#][type]" data-value="#type_value#" id="address_type_#i#" data-field="type" data-l10n-id="#type_l10n_id#">#type#</span>
</legend>
<section>
<p class="setbox-item">
<input placeholder="Street" data-field="streetAddress" data-l10n-id="streetAddress" name="adr[#i#][streetAddress]" class="textfield" type="text" x-inputmode="latin-prose" value="#streetAddress#" id="streetAddress_#i#">
<button type="reset">Clear</button>
</p>
<p class="setbox-item">
<input placeholder="Zip Code" data-field="postalCode" data-l10n-id="postalCode" name="adr[#i#][postalCode]" class="textfield" type="text" value="#postalCode#" id="postalCode_#i#">
<button type="reset">Clear</button>
</p>
<p class="setbox-item">
<input placeholder="City" data-field="locality" data-l10n-id="locality" name="adr[#i#][locality]" class="textfield" type="text" x-inputmode="latin-prose" value="#locality#" id="locality_#i#">
<button type="reset">Clear</button>
</p>
<p class="setbox-item">
<input placeholder="Country" data-field="countryName" data-l10n-id="countryName" name="adr[#i#][countryName]" class="textfield" type="text" x-inputmode="latin-prose" value="#countryName#" id="countryName_#i#">
<button type="reset">Clear</button>
</p>
</section>
</fieldset>
</div>
<!-- COMMENTS TEMPLATE -->
<div id='add-note-#i#' data-template class='note-template' data-index="#i#">
<dl class="setbox fillflow-row">
<dd class="setbox-body">
<p class="setbox-item">
<input placeholder="Comment" data-l10n-id="comment" name="comment[#i#]" class="textfield" type="text" x-inputmode="latin-prose" value="#note#" id="note_#i#">
<button type="reset">Clear</button>
</p>
</dd>
</dl>
</div>
<article class="view-body">
<div class="view-body-inner">
<form id="contact-form" action="#" class="fill-flow">
<input name="id" id="contact-form-id" type="hidden" value="">
<section class="item first">
<a id="thumbnail-action" href="#" class="item-media fillflow-media">
<p>
<span role="button" class="icon-addimage bb-button"></span>
<em data-l10n-id="addPhoto"> Add photo</em>
</p>
<p class="edit-photo">
<em data-l10n-id="edit">Edit</em>
</p>
<img id="thumbnail-photo" alt="">
</a>
<div class="item-body-exp">
<p class="fillflow-row">
<input placeholder="Name" data-l10n-id="givenName" name="givenName" class="textfield" type="text" x-inputmode="latin-prose" value="" id="givenName">
<button type="reset">Clear</button>
</p>
<p class="fillflow-row">
<input placeholder="Last name" data-l10n-id="familyName" name="familyName" class="textfield" type="text" x-inputmode="latin-prose" value="" id="familyName">
<button type="reset">Clear</button>
</p>
</div>
<p class="fillflow-row">
<input placeholder="Company Name" data-l10n-id="org" name="org" class="textfield" type="text" x-inputmode="latin-prose" value="" id="org">
<button type="reset">Clear</button>
</p>
</section>
<ul>
<li>
<section>
<div id="contacts-form-phones">
<!-- Iterating over phone template here -->
</div>
</section>
<button data-field-type="tel" id='add-new-phone' data-l10n-id="addPhone" class="fillflow-row action action-add icon icon-add">
Add phone number
</button>
</li>
<li>
<section>
<div id="contacts-form-emails">
<!-- Iterating over email template here -->
</div>
</section>
<button data-field-type="email" id='add-new-email' data-l10n-id="addEmail" class="fillflow-row action action-add icon icon-add">
Add email address
</button>
</li>
<li>
<section>
<div id="contacts-form-addresses">
<!-- Iterating over address template here -->
</div>
</section>
<button data-field-type="adr" id='add-new-address' data-l10n-id="addAddress" class="fillflow-row action action-add icon icon-add">
Add new address
</button>
</li>
<li>
<section>
<div id="contacts-form-notes">
<!-- Iterating over comment template here -->
</div>
</section>
<button data-field-type="note" id='add-new-note' data-l10n-id="addComment" class="fillflow-row action action-add icon icon-add">
Add comment
</button>
</li>
</ul>
</form>
<footer class="tail-actions hide">
<button id='delete-contact' class="fillflow-row action action-delete danger">
<b data-l10n-id="deleteContact">Delete contact</b>
</button>
</footer>
</div>
</article>
</section>
<!-- Tag selection -->
<section id="view-select-tag" role="region" class="view view-right view-select-tag">
<gaia-header id="settings-header" action="back">
<h1 data-l10n-id="label">Label</h1>
<button id="settings-done" data-l10n-id="done">Done</button>
</gaia-header>
<article class="view-body">
<form class="view-body-inner">
<ul id="tags-list" class="selection-list compact">
</ul>
<p>
<input class="textfield" type="text" placeholder="Custom Tag" value="" id='custom-tag' data-l10n-id="customTag">
<button type="reset">Clear</button>
</p>
</form>
</article>
</section>
<section data-theme="organic" id="view-settings" role="region" class="skin-organic view view-bottom theme-settings">
<gaia-header>
<h1 data-l10n-id="settings">Settings</h1>
<button id="settings-close" data-l10n-id="done">Done</button>
</gaia-header>
<article class="view-body" id="settings-article">
<section role="region" class="view-body-inner">
<ul data-type="list">
<li id="settingsOrder">
<gaia-switch name="order.lastname">
<label data-l10n-id="contactsOrderBy"></label>
</gaia-switch>
</li>
</ul>
<header>
<h2 data-l10n-id="transferContactsTitle">Transfer Contacts</h2>
</header>
<ul data-type="list" id="sources">
<li id="importContacts" data-source="sim">
<button class="icon icon-import" data-l10n-id="importContactsButton">
Import Contacts
</button>
</li>
<li id="exportContacts" data-source="sd">
<button class="icon icon-export" data-l10n-id="exportContactsButton">
Export Contacts
</button>
</li>
</ul>
<header>
<h2 data-l10n-id="ContactManagementTitle">Contacts Management</h2>
</header>
<ul data-type="list" id="contactsManagement">
<li>
<button id="set-ice" class="action action-delete" data-l10n-id="setICEButton">
Set ICE Contacts
</button>
</li>
<li>
<button id="bulkDelete" class=" fillflow-row action action-delete danger bulkdelete">
<b data-l10n-id="bulkDeleteButton">Bulk delete</b>
</button>
</li>
</ul>
</section>
</article>
</section>
<section data-theme="organic" id="import-settings" role="region" class="skin-organic view view-right theme-settings">
<gaia-header id="import-settings-header" action="back">
<h1 id='import-settings-title'></h1>
</gaia-header>
<article class="view-body">
<section role="region" class="view-body-inner">
<ul data-type="list" id="import-options">
<li id="import-sim-option" data-source="sim">
<button class="icon icon-sim">
<span data-l10n-id="importSim2">SIM card</span>
<p><span></span><time></time></p>
</button>
<p class="error-message" data-l10n-id="noSimMsg"></p>
</li>
<li id="import-sd-option" data-source="sd">
<button class="icon icon-sd">
<span data-l10n-id="importMemoryCard">Memory card</span>
<p><span></span><time></time></p>
</button>
<p class="error-message" data-l10n-id="noMemoryCardMsg"></p>
</li>
<li id="import-gmail-option" class="importService" data-source="gmail">
<button class="icon icon-gmail">
<span data-l10n-id="importGmail">Gmail</span>
<p><span></span><time></time></p>
</button>
</li>
<li id="import-live-option" class="importService" data-source="live">
<button class="icon icon-live">
<span data-l10n-id="importOutlook">Outlook</span>
<p><span></span><time></time></p>
</button>
<p class="error-message" data-l10n-id="genericNoConnection"></p>
</li>
</ul>
<ul data-type="list" id="export-options">
<li id="export-sim-option" data-source="sim">
<button class="icon icon-sim" data-l10n-id="simCard">
SIM card
</button>
<p class="error-message" data-l10n-id="noSimMsgExport"></p>
</li>
<li id="export-sd-option" data-source="sd">
<button class="icon icon-sd" data-l10n-id="memoryCard">
Memory card
</button>
<p class="error-message" data-l10n-id="noMemoryCardMsgExport"></p>
</li>
<li id="export-bluetooth-option" data-source="bluetooth">
<button class="icon icon-bluetooth" data-l10n-id="bluetooth">
Bluetooth
</button>
</li>
</ul>
</section>
</article>
</section>
<section data-theme="organic" id="ice-settings" role="region" class="skin-organic view view-right theme-settings">
<gaia-header id="ice-settings-header" action="back">
<h1 id='ice-settings-title' data-l10n-id="ICESettingTitle">ICE Contacts</h1>
</gaia-header>
<article class="view-body">
<section role="region" class="view-body-inner">
<p class="description" data-l10n-id="ICEDescription">
Add two contacts for making “In Case of Emergency” calls.
</p>
<ul data-type="list">
<li id="ice-contacts-1-switch">
<gaia-switch name="ice-contact-1-enabled">
<label data-l10n-id="ICEContact1"></label>
</gaia-switch>
</li>
<li>
<button id="select-ice-contact-1" class="icon icon-view" data-l10n-id="ICESelectContact" disabled>
Select a contact
</button>
</li>
<li id="ice-contacts-2-switch">
<gaia-switch name="ice-contact-2-enabled">
<label data-l10n-id="ICEContact2"></label>
</gaia-switch>
</li>
<li>
<button id="select-ice-contact-2" class="icon icon-view" data-l10n-id="ICESelectContact" disabled>
Select a contact
</button>
</li>
</ul>
</section>
</article>
</section>
<form data-type="confirm" id="loading-overlay" class="hide no-opacity" role="dialog">
<section id="progress-activity" class="hide">
<h1 id="progress-title"></h1>
<p role="status">
<span id="progress-msg"></span>
<progress id="progress-element" class="pack-activity" value="0" max="100"></progress>
</p>
</section>
</form>
<section id="statusMsg" role="status">
<p></p>
</section>
<form id="confirmation-message" class="hide" role="dialog" data-type="confirm">
<section>
<h1>Confirmation title</h1>
<p>Confirmation message</p>
</section>
<menu>
<button>Action 1</button>
<button>Action 2</button>
</menu>
</form>

View file

@ -0,0 +1,10 @@
'use strict';
/* exported MockContactsIndexHtml */
var MockContactsIndexHtml = (function MockLinkHtml() {
var req = new XMLHttpRequest();
req.open('GET', '/contacts/test/unit/mock_contacts_index.html', false);
req.send(null);
return req.responseText;
})();

View file

@ -0,0 +1,124 @@
'use strict';
/* exported MockContactsList */
function MockContactsList() {
return [
{
'id': '1',
'updated': new Date(),
'additionalName': [''],
'adr': [
{
'countryName': 'Germany',
'locality': 'Chemnitz',
'postalCode': '09034',
'streetAddress': 'Gotthardstrasse 22'
}
],
'bday': '1978-12-20',
'email': [
{
'type': 'Personal',
'value': 'test@test.com'
}
],
'familyName': ['AD'],
'givenName': ['Pepito'],
'jobTitle': [''],
'name': [
'Pepito A'
],
'org': ['Test'],
'tel': [
{
'value': '+346578888881',
'type': 'mobile',
'carrier': 'TEF'
}
],
'category': [],
'note': [
'Note 1'
],
'photo': ['test.png']
},
{
'id': '2',
'updated': new Date(),
'additionalName': [''],
'adr': [
{
'countryName': 'Germany',
'locality': 'Chemnitz',
'postalCode': '09034',
'streetAddress': 'Gotthardstrasse 22'
}
],
'bday': '1978-12-20',
'email': [
{
'type': 'Personal',
'value': 'test@test.com'
}
],
'familyName': ['BA'],
'givenName': ['Pepito'],
'jobTitle': [''],
'name': [
'Pepito BA'
],
'org': ['Test'],
'tel': [
{
'value': '+346578888882',
'type': 'mobile',
'carrier': 'TEF'
}
],
'category': [],
'note': [
'Note 1'
],
'photo': ['test.png']
},
{
'id': '3',
'updated': new Date(),
'additionalName': [''],
'adr': [
{
'countryName': 'Germany',
'locality': 'Chemnitz',
'postalCode': '09034',
'streetAddress': 'Gotthardstrasse 22'
}
],
'bday': '1978-12-20',
'email': [
{
'type': 'Personal',
'value': 'test@test.com'
}
],
'familyName': ['CC'],
'givenName': ['Antonio'],
'jobTitle': [''],
'name': [
'Antonio CC'
],
'org': ['Test'],
'tel': [
{
'value': '+346578888883',
'type': 'mobile',
'carrier': 'TEF'
}
],
'category': [],
'note': [
'Note 1'
],
'photo': ['test.png']
}
];
}

View file

@ -0,0 +1,19 @@
'use strict';
/* exported MockContactsListObj */
var MockContactsListObj = {
init: function() {},
load: function() {},
refresh: function() {},
refreshFb: function() {},
getContactById: function() {},
getAllContacts: function() {},
handleClick: function() {},
remove: function() {},
loaded: true,
clearClickHandlers: function() {},
setOrderByLastName: function() {},
initAlphaScroll: function() {},
isSelecting: false,
toggleICEGroup: function() {}
};

View file

@ -0,0 +1,8 @@
'use strict';
/* exported MockMatcher */
var MockMatcher = {
match: function(contact, mode, cbs) {
cbs.onmismatch();
}
};

View file

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

View file

@ -0,0 +1,15 @@
'use strict';
/* exported MockContactsSearch */
var MockContactsSearch = {
'init': function(s, d, n) {},
'invalidateCache': function() {},
'appendNodes': function(nodes) {},
'removeContact': function(id) {},
'search': function(cb) { cb(); },
'enterSearchMode': function(evt) {},
'exitSearchMode': function(evt) {},
'isInSearchMode': function() { return false; },
'enableSearch': function() {},
'selectRow': function(id, value) {}
};

View file

@ -0,0 +1,22 @@
'use strict';
/* exported MockContactsService */
var MockContactsService = {
addListener: function(event, handler) {
},
removeListener: function(event, handler) {
},
isEmpty: function(callback) {
},
save: function(contact, callback) {
},
remove: function(contact, callback) {
},
get: function (contactID, successCb, errorCb) {
},
getAll: function(callback) {
},
getAllStreamed: function(sortBy, onContactCB, onErrorCB, onCompleteCB) {
}
};

View file

@ -0,0 +1,13 @@
'use strict';
/* exported MockContactsSettings */
var MockContactsSettings = {
init: function() {},
close: function() {},
refresh: function() {},
cardStateChanged: function() {},
updateTimestamps: function() {},
navigation: {
go: function() {}
}
};

View file

@ -0,0 +1,17 @@
'use strict';
/* exported MockAlphaScroll */
var MockAlphaScroll = {
init: function(params) {
return;
},
toggleFormat: function() {
return;
},
showGroup: function(name) {
},
hideGroup: function(name) {
}
};

View file

@ -0,0 +1,11 @@
'use strict';
/* exported MockContactsTag */
var MockContactsTag = {
filterTags: function (type, currentNode, tags) {
return tags;
},
setCustomTag: function (element) {},
setCustomTagVisibility: function (value) {},
fillTagOptions: function (target, _originalTag, options) {}
};

View file

@ -0,0 +1,12 @@
'use strict';
/* exported MockCookie */
var MockCookie = {
data: {},
load: function() {
return this.data;
},
update: function(obj) {
this.data = obj;
}
};

View file

@ -0,0 +1,7 @@
'use strict';
/* exported MockDatastoreMigration */
var MockDatastoreMigration = function() {
this.start = function() {};
};

Some files were not shown because too many files have changed in this diff Show more