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,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);
});
});
});