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:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
3
gaia/apps/sms/views/shared/test/unit/.jshintrc
Executable file
3
gaia/apps/sms/views/shared/test/unit/.jshintrc
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"extends": "../../../../test/unit/.jshintrc"
|
||||
}
|
||||
423
gaia/apps/sms/views/shared/test/unit/activity_handler_test.js
Executable file
423
gaia/apps/sms/views/shared/test/unit/activity_handler_test.js
Executable file
|
|
@ -0,0 +1,423 @@
|
|||
/*global ActivityClient,
|
||||
ActivityHandler,
|
||||
App,
|
||||
Attachment,
|
||||
Drafts,
|
||||
MessageManager,
|
||||
MocksHelper,
|
||||
Navigation,
|
||||
Settings,
|
||||
Utils
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/test/unit/mock_attachment.js');
|
||||
require('/services/test/unit/mock_message_manager.js');
|
||||
require('/services/test/unit/activity/mock_activity_shim.js');
|
||||
require('/services/test/unit/activity/mock_activity_client.js');
|
||||
require('/views/shared/test/unit/mock_app.js');
|
||||
require('/views/shared/test/unit/mock_settings.js');
|
||||
require('/views/shared/test/unit/mock_navigation.js');
|
||||
require('/services/test/unit/mock_drafts.js');
|
||||
|
||||
require('/views/shared/js/utils.js');
|
||||
require('/views/shared/test/unit/mock_utils.js');
|
||||
|
||||
require('/views/shared/js/activity_handler.js');
|
||||
|
||||
var mocksHelperForActivityHandler = new MocksHelper([
|
||||
'ActivityClient',
|
||||
'ActivityShim',
|
||||
'App',
|
||||
'Attachment',
|
||||
'Draft',
|
||||
'Drafts',
|
||||
'MessageManager',
|
||||
'Navigation',
|
||||
'Settings',
|
||||
'Utils'
|
||||
]).init();
|
||||
|
||||
suite('ActivityHandler', function() {
|
||||
mocksHelperForActivityHandler.attachTestHelpers();
|
||||
|
||||
setup(function() {
|
||||
this.sinon.stub(Utils, 'onceDocumentIsVisible').returns(Promise.resolve());
|
||||
this.sinon.stub(Utils, 'alert').returns(Promise.resolve());
|
||||
|
||||
this.sinon.stub(ActivityClient);
|
||||
});
|
||||
|
||||
test('init()', function() {
|
||||
ActivityHandler.init();
|
||||
|
||||
sinon.assert.calledOnce(ActivityClient.init);
|
||||
sinon.assert.calledWith(ActivityClient.init, App.instanceId);
|
||||
|
||||
sinon.assert.calledWith(ActivityClient.on, 'new-activity-request');
|
||||
sinon.assert.calledWith(ActivityClient.on, 'share-activity-request');
|
||||
});
|
||||
|
||||
suite('"share" activity', function() {
|
||||
var activityData;
|
||||
|
||||
function onceShareActivityCompleted() {
|
||||
sinon.assert.called(ActivityHandler._onShareActivity);
|
||||
return ActivityHandler._onShareActivity.lastCall.returnValue;
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
this.sinon.spy(ActivityHandler, '_onShareActivity');
|
||||
this.sinon.spy(Navigation, 'toPanel');
|
||||
this.sinon.spy(Drafts, 'add');
|
||||
this.sinon.spy(Drafts, 'store');
|
||||
this.sinon.spy(Drafts, 'request');
|
||||
|
||||
activityData = {
|
||||
type: 'video/*',
|
||||
blobs: [
|
||||
new Blob(['test'], { type: 'video/x-video' }),
|
||||
new Blob(['test2'], { type: 'video/x-video' }),
|
||||
new Blob(),
|
||||
new Blob(),
|
||||
new Blob()
|
||||
],
|
||||
filenames: [
|
||||
'testBlob1', 'testBlob2', 'testBlob3', 'testBlob4', 'testBlob5'
|
||||
]
|
||||
};
|
||||
|
||||
ActivityHandler.init();
|
||||
});
|
||||
|
||||
test('moves to the composer panel after saving draft', function(done) {
|
||||
ActivityClient.on.withArgs('share-activity-request').yield(
|
||||
activityData
|
||||
);
|
||||
|
||||
onceShareActivityCompleted().then(() => {
|
||||
sinon.assert.calledWithMatch(Drafts.add, {
|
||||
recipients: null,
|
||||
type: 'mms',
|
||||
content: [
|
||||
sinon.match.instanceOf(Attachment),
|
||||
sinon.match.instanceOf(Attachment),
|
||||
sinon.match.instanceOf(Attachment),
|
||||
sinon.match.instanceOf(Attachment),
|
||||
sinon.match.instanceOf(Attachment)
|
||||
]
|
||||
});
|
||||
sinon.assert.called(Drafts.store);
|
||||
sinon.assert.callOrder(Drafts.request, Drafts.add, Drafts.store);
|
||||
sinon.assert.calledWith(
|
||||
Navigation.toPanel,
|
||||
'composer',
|
||||
{ draftId: 'draftId', focus: sinon.match.falsy }
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Attachment size over max mms should not be appended', function(done) {
|
||||
// Adjust mmsSizeLimitation for verifying alert popup when size over
|
||||
// limitation.
|
||||
Settings.mmsSizeLimitation = 1;
|
||||
|
||||
ActivityClient.on.withArgs('share-activity-request').yield(
|
||||
activityData
|
||||
);
|
||||
|
||||
sinon.assert.calledWith(Utils.alert, {
|
||||
id: 'attached-files-too-large',
|
||||
args: { n: 5, mmsSize: '0' }
|
||||
});
|
||||
|
||||
Utils.alert.lastCall.returnValue.then(() => {
|
||||
sinon.assert.calledWithExactly(ActivityClient.postResult);
|
||||
sinon.assert.notCalled(Navigation.toPanel);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Should append images even when they are big', function(done) {
|
||||
activityData.blobs = [new Blob(['test'], { type: 'image/jpeg' })];
|
||||
|
||||
Settings.mmsSizeLimitation = 1;
|
||||
|
||||
ActivityClient.on.withArgs('share-activity-request').yield(
|
||||
activityData
|
||||
);
|
||||
|
||||
sinon.assert.notCalled(Utils.alert);
|
||||
|
||||
onceShareActivityCompleted().then(() => {
|
||||
sinon.assert.calledWithMatch(Drafts.add, {
|
||||
recipients: null,
|
||||
type: 'mms',
|
||||
content: [sinon.match.instanceOf(Attachment)]
|
||||
});
|
||||
sinon.assert.called(Drafts.store);
|
||||
sinon.assert.callOrder(Drafts.request, Drafts.add, Drafts.store);
|
||||
sinon.assert.calledWith(
|
||||
Navigation.toPanel,
|
||||
'composer',
|
||||
{ draftId: 'draftId', focus: sinon.match.falsy }
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Should append vcard attachment', function(done) {
|
||||
activityData.blobs = [new Blob(['test'], { type: 'text/x-vcard' })];
|
||||
|
||||
ActivityClient.on.withArgs('share-activity-request').yield(
|
||||
activityData
|
||||
);
|
||||
|
||||
sinon.assert.notCalled(Utils.alert);
|
||||
|
||||
onceShareActivityCompleted().then(() => {
|
||||
sinon.assert.calledWithMatch(Drafts.add, {
|
||||
recipients: null,
|
||||
type: 'mms',
|
||||
content: [sinon.match.instanceOf(Attachment)]
|
||||
});
|
||||
sinon.assert.called(Drafts.store);
|
||||
sinon.assert.callOrder(Drafts.request, Drafts.add, Drafts.store);
|
||||
sinon.assert.calledWith(
|
||||
Navigation.toPanel,
|
||||
'composer',
|
||||
{ draftId: 'draftId', focus: sinon.match.falsy }
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Appends URL to the Compose field for activity with URL data type',
|
||||
function(done) {
|
||||
var urlActivityData = {
|
||||
type: 'url',
|
||||
url: 'test_url'
|
||||
};
|
||||
|
||||
ActivityClient.on.withArgs('share-activity-request').yield(
|
||||
urlActivityData
|
||||
);
|
||||
|
||||
sinon.assert.notCalled(Utils.alert);
|
||||
|
||||
onceShareActivityCompleted().then(() => {
|
||||
sinon.assert.calledWithMatch(Drafts.add, {
|
||||
recipients: null,
|
||||
type: 'sms',
|
||||
content: [urlActivityData.url]
|
||||
});
|
||||
sinon.assert.called(Drafts.store);
|
||||
sinon.assert.callOrder(Drafts.request, Drafts.add, Drafts.store);
|
||||
sinon.assert.calledWith(
|
||||
Navigation.toPanel,
|
||||
'composer',
|
||||
{ draftId: 'draftId', focus: sinon.match.falsy }
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Call activity postError if no data to share', function() {
|
||||
ActivityClient.on.withArgs('share-activity-request').yield({
|
||||
type: 'url'
|
||||
});
|
||||
|
||||
sinon.assert.notCalled(Navigation.toPanel);
|
||||
sinon.assert.notCalled(ActivityClient.postResult);
|
||||
sinon.assert.calledOnce(ActivityClient.postError);
|
||||
sinon.assert.calledWith(
|
||||
ActivityClient.postError, 'No data to share found!'
|
||||
);
|
||||
});
|
||||
|
||||
test('Call activity postError on unknown activity data type', function() {
|
||||
ActivityClient.on.withArgs('share-activity-request').yield({
|
||||
type: 'multipart/mixed'
|
||||
});
|
||||
|
||||
sinon.assert.notCalled(Navigation.toPanel);
|
||||
sinon.assert.notCalled(ActivityClient.postResult);
|
||||
sinon.assert.calledOnce(ActivityClient.postError);
|
||||
sinon.assert.calledWith(
|
||||
ActivityClient.postError,
|
||||
'Unsupported activity data type: multipart/mixed'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('"new" activity', function() {
|
||||
var emailActivityData;
|
||||
|
||||
function onceNewActivityCompleted() {
|
||||
sinon.assert.called(ActivityHandler._onNewActivity);
|
||||
return ActivityHandler._onNewActivity.lastCall.returnValue;
|
||||
}
|
||||
|
||||
function assertDraftSaved(draft) {
|
||||
sinon.assert.calledWithMatch(Drafts.add, draft);
|
||||
sinon.assert.called(Drafts.store);
|
||||
sinon.assert.callOrder(Drafts.request, Drafts.add, Drafts.store);
|
||||
}
|
||||
|
||||
var threadDeferred;
|
||||
|
||||
setup(function() {
|
||||
emailActivityData = {
|
||||
target: 'abc@exmple.com',
|
||||
body: 'foo'
|
||||
};
|
||||
|
||||
// configure findThreadFromNumber
|
||||
threadDeferred = Utils.Promise.defer();
|
||||
this.sinon.stub(MessageManager, 'findThreadFromNumber').returns(
|
||||
threadDeferred.promise
|
||||
);
|
||||
this.sinon.spy(Navigation, 'toPanel');
|
||||
this.sinon.spy(Drafts, 'add');
|
||||
this.sinon.spy(Drafts, 'store');
|
||||
this.sinon.spy(Drafts, 'request');
|
||||
this.sinon.spy(ActivityHandler, '_onNewActivity');
|
||||
|
||||
ActivityHandler.init();
|
||||
});
|
||||
|
||||
test('Non-existing thread, with number and body', function(done) {
|
||||
ActivityClient.on.withArgs('new-activity-request').yield({
|
||||
number: '123',
|
||||
body: 'foo'
|
||||
});
|
||||
|
||||
// This should not be used but keeping it here to have a consistent
|
||||
// environment for this test case.
|
||||
threadDeferred.reject(new Error('No thread for this test'));
|
||||
|
||||
onceNewActivityCompleted().then(function() {
|
||||
sinon.assert.notCalled(MessageManager.findThreadFromNumber);
|
||||
|
||||
assertDraftSaved({
|
||||
recipients: ['123'],
|
||||
type: 'sms',
|
||||
content: ['foo']
|
||||
});
|
||||
sinon.assert.calledWith(
|
||||
Navigation.toPanel,
|
||||
'composer',
|
||||
{ draftId: 'draftId', focus: 'composer' }
|
||||
);
|
||||
}).then(done,done);
|
||||
});
|
||||
|
||||
test('existing thread with specified body, navigate to composer',
|
||||
function(done) {
|
||||
ActivityClient.on.withArgs('new-activity-request').yield({
|
||||
number: '123',
|
||||
body: 'foo'
|
||||
});
|
||||
|
||||
// This should not be used but keeping it here to have a consistent
|
||||
// environment for this test case.
|
||||
threadDeferred.resolve(42);
|
||||
|
||||
onceNewActivityCompleted().then(function() {
|
||||
sinon.assert.notCalled(MessageManager.findThreadFromNumber);
|
||||
|
||||
assertDraftSaved({
|
||||
recipients: ['123'],
|
||||
type: 'sms',
|
||||
content: ['foo']
|
||||
});
|
||||
|
||||
sinon.assert.calledWithMatch(
|
||||
Navigation.toPanel,
|
||||
'composer', { draftId: 'draftId' }
|
||||
);
|
||||
}).then(done,done);
|
||||
});
|
||||
|
||||
test('new message with body only', function(done) {
|
||||
ActivityClient.on.withArgs('new-activity-request').yield(
|
||||
{ body: 'foo' }
|
||||
);
|
||||
|
||||
onceNewActivityCompleted().then(() => {
|
||||
sinon.assert.notCalled(MessageManager.findThreadFromNumber);
|
||||
|
||||
assertDraftSaved({
|
||||
recipients: null,
|
||||
type: 'sms',
|
||||
content: ['foo']
|
||||
});
|
||||
sinon.assert.calledWith(
|
||||
Navigation.toPanel,
|
||||
'composer',
|
||||
{ draftId: 'draftId', focus: sinon.match.falsy }
|
||||
);
|
||||
}).then(done,done);
|
||||
});
|
||||
|
||||
test('Non-existing thread with recipient only, navigate to composer',
|
||||
function(done) {
|
||||
ActivityClient.on.withArgs('new-activity-request').yield({
|
||||
number: '123'
|
||||
});
|
||||
|
||||
threadDeferred.reject(new Error('No thread for this test'));
|
||||
|
||||
onceNewActivityCompleted().then(function() {
|
||||
sinon.assert.calledWith(MessageManager.findThreadFromNumber, '123');
|
||||
assertDraftSaved({
|
||||
recipients: ['123'],
|
||||
type: 'sms',
|
||||
content: null
|
||||
});
|
||||
sinon.assert.calledWith(
|
||||
Navigation.toPanel,
|
||||
'composer',
|
||||
{ draftId: 'draftId', focus: 'composer' }
|
||||
);
|
||||
}).then(done,done);
|
||||
});
|
||||
|
||||
test('new message with email and body', function(done) {
|
||||
ActivityClient.on.withArgs('new-activity-request').yield(
|
||||
emailActivityData
|
||||
);
|
||||
|
||||
// This is not used but it's kept here for consistency.
|
||||
threadDeferred.reject(new Error('No thread for this test'));
|
||||
|
||||
onceNewActivityCompleted().then(function() {
|
||||
sinon.assert.notCalled(MessageManager.findThreadFromNumber);
|
||||
|
||||
assertDraftSaved({
|
||||
recipients: [emailActivityData.target],
|
||||
type: 'mms',
|
||||
content: [emailActivityData.body]
|
||||
});
|
||||
sinon.assert.calledWith(
|
||||
Navigation.toPanel,
|
||||
'composer',
|
||||
{ draftId: 'draftId', focus: 'composer' }
|
||||
);
|
||||
}).then(done,done);
|
||||
});
|
||||
|
||||
test('existing thread with recipient only, navigate to the thread',
|
||||
function(done) {
|
||||
ActivityClient.on.withArgs('new-activity-request').yield({
|
||||
number: '123'
|
||||
});
|
||||
|
||||
// this time we can find a thread with id=42
|
||||
threadDeferred.resolve(42);
|
||||
|
||||
onceNewActivityCompleted().then(function() {
|
||||
sinon.assert.calledWithMatch(
|
||||
Navigation.toPanel, 'thread', { id: 42, focus: 'composer' }
|
||||
);
|
||||
}).then(done,done);
|
||||
});
|
||||
});
|
||||
});
|
||||
255
gaia/apps/sms/views/shared/test/unit/activity_picker_test.js
Executable file
255
gaia/apps/sms/views/shared/test/unit/activity_picker_test.js
Executable file
|
|
@ -0,0 +1,255 @@
|
|||
/*global MocksHelper, MockL10n, ActivityPicker, MozActivity */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l20n.js');
|
||||
|
||||
require('/views/shared/js/activity_picker.js');
|
||||
require('/views/shared/js/utils.js');
|
||||
|
||||
require('/views/shared/test/unit/mock_moz_activity.js');
|
||||
require('/views/shared/test/unit/mock_utils.js');
|
||||
|
||||
|
||||
var mocksHelperAP = new MocksHelper([
|
||||
'MozActivity',
|
||||
'Utils'
|
||||
]).init();
|
||||
|
||||
suite('ActivityPicker', function() {
|
||||
var realMozL10n, onsuccess, onerror;
|
||||
|
||||
suiteSetup(function() {
|
||||
realMozL10n = document.l10n;
|
||||
document.l10n = MockL10n;
|
||||
mocksHelperAP.suiteSetup();
|
||||
|
||||
onsuccess = function() {};
|
||||
onerror = function() {};
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
document.l10n = realMozL10n;
|
||||
mocksHelperAP.suiteTeardown();
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
mocksHelperAP.setup();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
mocksHelperAP.teardown();
|
||||
});
|
||||
|
||||
suite('dial', function() {
|
||||
|
||||
test('dial(number) ', function() {
|
||||
ActivityPicker.dial('999');
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'dial',
|
||||
data: { type: 'webtelephony/number', number: '999' }
|
||||
});
|
||||
});
|
||||
|
||||
test('dial(number, success) ', function() {
|
||||
ActivityPicker.dial('999', onsuccess);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onsuccess, onsuccess
|
||||
);
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'dial',
|
||||
data: { type: 'webtelephony/number', number: '999' }
|
||||
});
|
||||
});
|
||||
|
||||
test('dial(number, success, error) ', function() {
|
||||
ActivityPicker.dial('999', onsuccess, onerror);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onsuccess, onsuccess
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onerror, onerror
|
||||
);
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'dial',
|
||||
data: { type: 'webtelephony/number', number: '999' }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('email', function() {
|
||||
|
||||
test('email(email) ', function() {
|
||||
ActivityPicker.email('a@b.com');
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'mail',
|
||||
URI: 'mailto:a@b.com'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('email(email, success) ', function() {
|
||||
ActivityPicker.email('a@b.com', onsuccess);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onsuccess, onsuccess
|
||||
);
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'mail',
|
||||
URI: 'mailto:a@b.com'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('email(email, success, error) ', function() {
|
||||
ActivityPicker.email('a@b.com', onsuccess, onerror);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onsuccess, onsuccess
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onerror, onerror
|
||||
);
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'mail',
|
||||
URI: 'mailto:a@b.com'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('url', function() {
|
||||
|
||||
test('url(url) ', function() {
|
||||
ActivityPicker.url('http://mozilla.com');
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: 'http://mozilla.com'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('url(url, success) ', function() {
|
||||
ActivityPicker.url('http://mozilla.com', onsuccess);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onsuccess, onsuccess
|
||||
);
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: 'http://mozilla.com'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('url(url, success, error) ', function() {
|
||||
ActivityPicker.url('http://mozilla.com', onsuccess, onerror);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onsuccess, onsuccess
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
MozActivity.instances[0].onerror, onerror
|
||||
);
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: 'http://mozilla.com'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('createNewContact', function() {
|
||||
ActivityPicker.createNewContact({foo: ['bar']});
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: { foo: ['bar'] }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('addToExistingContact', function() {
|
||||
ActivityPicker.addToExistingContact({foo: ['bar']});
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'update',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: { foo: ['bar'] }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('viewContact', function() {
|
||||
ActivityPicker.viewContact({foo: ['bar']});
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], {
|
||||
name: 'open',
|
||||
data: {
|
||||
type: 'webcontacts/contact',
|
||||
params: { foo: ['bar'] }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite('openSettings', function() {
|
||||
var params = {
|
||||
name: 'configure',
|
||||
data: {
|
||||
target: 'device',
|
||||
section: 'messaging'
|
||||
}
|
||||
};
|
||||
|
||||
test('openSettings() ', function() {
|
||||
ActivityPicker.openSettings();
|
||||
|
||||
assert.deepEqual(MozActivity.calls[0], params);
|
||||
});
|
||||
|
||||
test('openSettings(success) ', function() {
|
||||
ActivityPicker.openSettings(onsuccess);
|
||||
|
||||
assert.equal(MozActivity.instances[0].onsuccess, onsuccess);
|
||||
assert.deepEqual(MozActivity.calls[0], params);
|
||||
});
|
||||
|
||||
test('openSettings(success, error) ', function() {
|
||||
ActivityPicker.openSettings(onsuccess, onerror);
|
||||
|
||||
assert.equal(MozActivity.instances[0].onsuccess, onsuccess);
|
||||
assert.equal(MozActivity.instances[0].onerror, onerror);
|
||||
assert.deepEqual(MozActivity.calls[0], params);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
89
gaia/apps/sms/views/shared/test/unit/app_test.js
Executable file
89
gaia/apps/sms/views/shared/test/unit/app_test.js
Executable file
|
|
@ -0,0 +1,89 @@
|
|||
/*global App, Promise */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/js/app.js');
|
||||
|
||||
suite('App', function() {
|
||||
// Taken from app.js
|
||||
const APPLICATION_READY_CLASS_NAME = 'js-app-ready';
|
||||
|
||||
teardown(function() {
|
||||
document.body.classList.remove(APPLICATION_READY_CLASS_NAME);
|
||||
});
|
||||
|
||||
test('isReady is false by default', function() {
|
||||
assert.equal(App.isReady(), false);
|
||||
});
|
||||
|
||||
test('setReady sets body class and isReady', function() {
|
||||
App.setReady();
|
||||
|
||||
assert.equal(App.isReady(), true);
|
||||
assert.ok(document.body.classList.contains(APPLICATION_READY_CLASS_NAME));
|
||||
});
|
||||
|
||||
test('setReady throws exception if called more than once', function() {
|
||||
App.setReady();
|
||||
|
||||
assert.throws(function() {
|
||||
App.setReady();
|
||||
});
|
||||
});
|
||||
|
||||
test('whenReady is resolved immediately if app is ready', function(done) {
|
||||
App.setReady();
|
||||
App.whenReady().then(function() {
|
||||
assert.ok(App.isReady());
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('whenReady is resolved when setReady is called', function(done) {
|
||||
var stub = sinon.stub();
|
||||
this.sinon.spy(App, 'setReady');
|
||||
|
||||
var whenReadyPromise = App.whenReady();
|
||||
whenReadyPromise.then(stub).then(function() {
|
||||
assert.ok(App.isReady());
|
||||
sinon.assert.callOrder(App.setReady, stub);
|
||||
}).then(done, done);
|
||||
|
||||
Promise.resolve().then(function() {
|
||||
App.setReady();
|
||||
});
|
||||
});
|
||||
|
||||
test('whenReady is rejected in case of error', function(done) {
|
||||
var error = new Error('Test error');
|
||||
|
||||
this.sinon.stub(MutationObserver.prototype, 'observe', function() {
|
||||
throw error;
|
||||
});
|
||||
|
||||
App.whenReady().then(function() {
|
||||
throw new Error('Success callback should not have been called.');
|
||||
}, function(e) {
|
||||
assert.equal(e, error);
|
||||
}).then(done, done);
|
||||
|
||||
App.setReady();
|
||||
});
|
||||
|
||||
test('whenReady is rejected in case of error in MutationObserver callback',
|
||||
function(done) {
|
||||
var error = new Error('Test error');
|
||||
|
||||
// This is called inside MutationObserver callback
|
||||
this.sinon.stub(MutationObserver.prototype, 'disconnect', function() {
|
||||
throw error;
|
||||
});
|
||||
|
||||
App.whenReady().then(function() {
|
||||
throw new Error('Success callback should not have been called.');
|
||||
}, function(e) {
|
||||
assert.equal(e, error);
|
||||
}).then(done, done);
|
||||
|
||||
App.setReady();
|
||||
});
|
||||
});
|
||||
61
gaia/apps/sms/views/shared/test/unit/assets_helper.js
Executable file
61
gaia/apps/sms/views/shared/test/unit/assets_helper.js
Executable file
|
|
@ -0,0 +1,61 @@
|
|||
/*global Promise */
|
||||
|
||||
/*exported AssetsHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
var AssetsHelper = {
|
||||
generateCanvas: function(width, height) {
|
||||
var canvas = document.createElement('canvas'),
|
||||
context = canvas.getContext('2d');
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
var linearGradient = context.createLinearGradient(0, 0, width, height);
|
||||
linearGradient.addColorStop(0, 'blue');
|
||||
linearGradient.addColorStop(1, 'red');
|
||||
|
||||
context.fillStyle = linearGradient;
|
||||
context.fillRect (0, 0, width, height);
|
||||
|
||||
return canvas;
|
||||
},
|
||||
|
||||
generateImageDataURL: function(width, height, type, quality) {
|
||||
var canvas = null;
|
||||
try {
|
||||
canvas = this.generateCanvas(width, height);
|
||||
return canvas.toDataURL(type, quality);
|
||||
} finally {
|
||||
canvas.width = canvas.height = 0;
|
||||
canvas = null;
|
||||
}
|
||||
},
|
||||
|
||||
generateImageBlob: function(width, height, type, quality) {
|
||||
var canvas = this.generateCanvas(width, height);
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
canvas.toBlob(function(blob) {
|
||||
canvas.width = canvas.height = 0;
|
||||
canvas = null;
|
||||
|
||||
resolve(blob);
|
||||
}, type, quality);
|
||||
});
|
||||
},
|
||||
|
||||
loadFileBlob: function(filePath) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', filePath, true);
|
||||
xhr.responseType = 'blob';
|
||||
xhr.onload = function() {
|
||||
resolve(xhr.response);
|
||||
};
|
||||
xhr.onerror = reject;
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
};
|
||||
891
gaia/apps/sms/views/shared/test/unit/contact_renderer_test.js
Executable file
891
gaia/apps/sms/views/shared/test/unit/contact_renderer_test.js
Executable file
|
|
@ -0,0 +1,891 @@
|
|||
|
||||
/*global ContactRenderer, loadBodyHTML, MockContact, MockL10n, MocksHelper,
|
||||
Utils, Template, MockContactPhotoHelper, SharedComponents,
|
||||
MockSettings,
|
||||
AssetsHelper
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/js/shared_components.js');
|
||||
require('/views/shared/js/utils.js');
|
||||
require('/views/shared/test/unit/mock_utils.js');
|
||||
|
||||
require('/views/shared/test/unit/mock_contact.js');
|
||||
require('/shared/test/unit/mocks/mock_l20n.js');
|
||||
require('/shared/test/unit/mocks/mock_contact_photo_helper.js');
|
||||
require('/views/shared/js/contact_renderer.js');
|
||||
require('/views/shared/test/unit/mock_settings.js');
|
||||
|
||||
var mocksHelperForContactRenderer = new MocksHelper([
|
||||
'Utils',
|
||||
'ContactPhotoHelper',
|
||||
'Settings'
|
||||
]).init();
|
||||
|
||||
suite('ContactRenderer', function() {
|
||||
var realMozL10n;
|
||||
|
||||
var ul, contact;
|
||||
var testImageBlob;
|
||||
var renderer, unknownRender;
|
||||
|
||||
mocksHelperForContactRenderer.attachTestHelpers();
|
||||
|
||||
suiteSetup(function(done) {
|
||||
realMozL10n = document.l10n;
|
||||
document.l10n = MockL10n;
|
||||
|
||||
AssetsHelper.generateImageBlob(400, 400, 'image/jpeg', 0.5).then(
|
||||
(blob) => {
|
||||
testImageBlob = blob;
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
document.l10n = realMozL10n;
|
||||
realMozL10n = null;
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
loadBodyHTML('/index.html');
|
||||
|
||||
this.sinon.spy(Template.prototype, 'interpolate');
|
||||
ul = document.createElement('ul');
|
||||
contact = MockContact();
|
||||
});
|
||||
|
||||
suite('suggestion', function() {
|
||||
setup(function() {
|
||||
renderer = ContactRenderer.flavor('suggestion');
|
||||
});
|
||||
|
||||
test('Rendered Contact "givenName familyName"', function() {
|
||||
var html;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
|
||||
sinon.assert.calledWithMatch(Template.prototype.interpolate, {
|
||||
carrier: 'TEF',
|
||||
name: 'Pepito O\'Hare',
|
||||
nameHTML: 'Pepito O'Hare',
|
||||
number: '+346578888888',
|
||||
phoneDetailsHTML: SharedComponents.phoneDetails(
|
||||
{
|
||||
number: '+346578888888',
|
||||
type: 'Mobile',
|
||||
carrier: 'TEF'
|
||||
}
|
||||
).toString(),
|
||||
photoHTML: '',
|
||||
type: 'Mobile'
|
||||
});
|
||||
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
assert.include(html, 'Pepito O\'Hare');
|
||||
});
|
||||
|
||||
test('Rendered Contact highlighted "givenName familyName"', function() {
|
||||
var html;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'Pepito O\'Hare',
|
||||
target: ul
|
||||
});
|
||||
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
|
||||
assert.include(html, '<span class="highlight">Pepito</span>');
|
||||
assert.include(html, '<span class="highlight">O\'Hare</span>');
|
||||
});
|
||||
|
||||
test('Rendered Contact "number"', function() {
|
||||
var html;
|
||||
|
||||
contact.tel[0].carrier = null;
|
||||
contact.tel[0].type = null;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
|
||||
assert.ok(html.includes('+346578888888'));
|
||||
});
|
||||
|
||||
test('Rendered Contact highlighted "number"', function() {
|
||||
var html;
|
||||
|
||||
contact.tel[0].carrier = null;
|
||||
contact.tel[0].type = null;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '346578888888',
|
||||
target: ul
|
||||
});
|
||||
|
||||
sinon.assert.calledWithMatch(Template.prototype.interpolate, {
|
||||
carrier: '',
|
||||
name: 'Pepito O\'Hare',
|
||||
nameHTML: 'Pepito O'Hare',
|
||||
number: '+346578888888',
|
||||
phoneDetailsHTML: SharedComponents.phoneDetails(
|
||||
{ number: '+<span class="highlight">346578888888</span>' },
|
||||
{ safe: ['number'] }
|
||||
).toString(),
|
||||
photoHTML: '',
|
||||
type: ''
|
||||
});
|
||||
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
|
||||
assert.ok(
|
||||
html.includes('+<span class="highlight">346578888888</span>')
|
||||
);
|
||||
});
|
||||
|
||||
test('Rendered Contact highlighted "name number"', function() {
|
||||
var html;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'Pepito 346578888888',
|
||||
target: ul
|
||||
});
|
||||
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
|
||||
assert.include(html, '<span class="highlight">Pepito</span>');
|
||||
assert.include(html, '+<span class="highlight">346578888888</span>');
|
||||
});
|
||||
|
||||
test('Rendered Contact with type and number', function() {
|
||||
this.sinon.spy(SharedComponents, 'phoneDetails');
|
||||
|
||||
contact.tel[0].carrier = null;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
|
||||
sinon.assert.calledWith(
|
||||
SharedComponents.phoneDetails,
|
||||
{
|
||||
number: contact.tel[0].value,
|
||||
type: contact.tel[0].type[0],
|
||||
carrier: ''
|
||||
},
|
||||
{ safe: ['number'] }
|
||||
);
|
||||
});
|
||||
|
||||
test('Rendered Contact with type and highlighted number', function() {
|
||||
this.sinon.spy(SharedComponents, 'phoneDetails');
|
||||
|
||||
contact.tel[0].carrier = null;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '346578888888',
|
||||
target: ul
|
||||
});
|
||||
|
||||
sinon.assert.calledWith(
|
||||
SharedComponents.phoneDetails,
|
||||
{
|
||||
number: '+<span class="highlight">346578888888</span>',
|
||||
type: contact.tel[0].type[0],
|
||||
carrier: ''
|
||||
},
|
||||
{ safe: ['number'] }
|
||||
);
|
||||
});
|
||||
|
||||
test('Rendered Contact with type, number and carrier', function() {
|
||||
this.sinon.spy(SharedComponents, 'phoneDetails');
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
|
||||
sinon.assert.calledWith(
|
||||
SharedComponents.phoneDetails,
|
||||
{
|
||||
number: contact.tel[0].value,
|
||||
type: contact.tel[0].type[0],
|
||||
carrier: contact.tel[0].carrier
|
||||
},
|
||||
{ safe: ['number'] }
|
||||
);
|
||||
});
|
||||
|
||||
test('Rendered Contact with type, highlighted number and carrier',
|
||||
function() {
|
||||
this.sinon.spy(SharedComponents, 'phoneDetails');
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '346578888888',
|
||||
target: ul
|
||||
});
|
||||
|
||||
sinon.assert.calledWith(
|
||||
SharedComponents.phoneDetails,
|
||||
{
|
||||
number: '+<span class="highlight">346578888888</span>',
|
||||
type: contact.tel[0].type[0],
|
||||
carrier: contact.tel[0].carrier
|
||||
},
|
||||
{ safe: ['number'] }
|
||||
);
|
||||
});
|
||||
|
||||
test('Rendered Contact "email"', function() {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var html;
|
||||
|
||||
contact.email[0].type = null;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
|
||||
html = ul.lastElementChild.innerHTML;
|
||||
|
||||
assert.ok(html.includes('a@b.com'));
|
||||
});
|
||||
|
||||
test('Rendered Contact highlighted "email"', function() {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var html;
|
||||
|
||||
contact.email[0].type = null;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'a@b.com',
|
||||
target: ul
|
||||
});
|
||||
|
||||
sinon.assert.calledWithMatch(Template.prototype.interpolate, {
|
||||
carrier: '',
|
||||
name: 'Pepito O\'Hare',
|
||||
nameHTML: 'Pepito O'Hare',
|
||||
number: 'a@b.com',
|
||||
numberHTML: '<span class="highlight">a@b.com</span>',
|
||||
photoHTML: '',
|
||||
type: ''
|
||||
});
|
||||
|
||||
html = ul.lastElementChild.innerHTML;
|
||||
|
||||
assert.ok(
|
||||
html.includes('<span class="highlight">a@b.com</span>')
|
||||
);
|
||||
});
|
||||
|
||||
test('Rendered Contact highlighted "name email"', function() {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var html;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'Pepito a@b.com',
|
||||
target: ul
|
||||
});
|
||||
|
||||
html = ul.lastElementChild.innerHTML;
|
||||
|
||||
assert.include(html, '<span class="highlight">Pepito</span>');
|
||||
assert.include(html, '<span class="highlight">a@b.com</span>');
|
||||
});
|
||||
|
||||
test('Rendered Contact "type | email"', function() {
|
||||
var html;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
|
||||
html = ul.lastElementChild.innerHTML;
|
||||
|
||||
assert.isFalse(html.includes(
|
||||
'<span data-l10n-id="Personal">Personal</span> | ' + 'a@b.com'));
|
||||
});
|
||||
|
||||
test('Rendered Contact highlighted "type | email"', function() {
|
||||
var html;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'a@b.com',
|
||||
target: ul
|
||||
});
|
||||
|
||||
html = ul.lastElementChild.innerHTML;
|
||||
|
||||
assert.isFalse(html.includes(
|
||||
'<span data-l10n-id="Personal">Personal</span> | ' +
|
||||
'<span class="highlight">a@b.com</span>'
|
||||
));
|
||||
});
|
||||
|
||||
test('Rendered Contact w/ multiple: all (isSuggestion) ' +
|
||||
'and No Support EmailRecipient ',
|
||||
function() {
|
||||
MockSettings.supportEmailRecipient = false;
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '+12125559999',
|
||||
target: ul
|
||||
});
|
||||
|
||||
assert.equal(ul.children.length, 2);
|
||||
});
|
||||
|
||||
test('Rendered Contact w/ multiple: all (isSuggestion) ' +
|
||||
'and Support EmailRecipient ',
|
||||
function() {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '+12125559999',
|
||||
target: ul
|
||||
});
|
||||
|
||||
assert.equal(ul.children.length, 3);
|
||||
});
|
||||
|
||||
test('Rendered Contact omit numbers already in recipient list ' +
|
||||
'and No Support EmailRecipient ',
|
||||
function() {
|
||||
MockSettings.supportEmailRecipient = false;
|
||||
var html;
|
||||
|
||||
var skip = ['+346578888888'];
|
||||
|
||||
// This contact has two tel entries.
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '+346578888888',
|
||||
target: ul,
|
||||
skip: skip
|
||||
});
|
||||
|
||||
html = ul.innerHTML;
|
||||
|
||||
assert.ok(!html.includes('346578888888'));
|
||||
assert.equal(ul.children.length, 1);
|
||||
});
|
||||
|
||||
test('Rendered Contact omit numbers already in recipient list ' +
|
||||
'and Support EmailRecipient ',
|
||||
function() {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var html;
|
||||
|
||||
var skip = ['+346578888888'];
|
||||
|
||||
// This contact has two tel entries.
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '+346578888888',
|
||||
target: ul,
|
||||
skip: skip
|
||||
});
|
||||
|
||||
html = ul.innerHTML;
|
||||
|
||||
assert.ok(!html.includes('346578888888'));
|
||||
assert.equal(ul.children.length, 2);
|
||||
});
|
||||
|
||||
test('Rendered Contact omit emails already in recipient list ' +
|
||||
'and Support EmailRecipient ',
|
||||
function() {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var html;
|
||||
|
||||
var skip = ['a@b.com'];
|
||||
|
||||
// This contact has three tel entries.
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'a@b.com',
|
||||
target: ul,
|
||||
skip: skip
|
||||
});
|
||||
|
||||
html = ul.innerHTML;
|
||||
|
||||
assert.ok(!html.includes('a@b.com'));
|
||||
assert.equal(ul.children.length, 2);
|
||||
});
|
||||
|
||||
test('does not include photo', function() {
|
||||
var html;
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
|
||||
assert.isFalse(html.includes('span[data-type=img]'));
|
||||
});
|
||||
|
||||
test('append information block in the li', function() {
|
||||
var li;
|
||||
var block = document.createElement('div');
|
||||
var selector = '.suggestion';
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: 'Additional info',
|
||||
infoBlock: block,
|
||||
infoBlockParentSelector: selector,
|
||||
target: ul
|
||||
});
|
||||
li = ul.lastElementChild;
|
||||
|
||||
assert.isTrue(!!li.querySelector(selector));
|
||||
assert.equal(li.querySelector(selector).lastElementChild, block);
|
||||
});
|
||||
|
||||
test('Rendered no "Tel" and No Support EmailRecipient ', function() {
|
||||
|
||||
contact.tel = null;
|
||||
MockSettings.supportEmailRecipient = false;
|
||||
var result = renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
assert.isFalse(result);
|
||||
});
|
||||
|
||||
test('Rendered no "email" and No Support EmailRecipient ', function() {
|
||||
|
||||
contact.email = null;
|
||||
MockSettings.supportEmailRecipient = false;
|
||||
var result = renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
assert.ok(result);
|
||||
});
|
||||
|
||||
test('Rendered no "Tel" and Support EmailRecipient ', function() {
|
||||
|
||||
contact.tel = null;
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var result = renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
assert.ok(result);
|
||||
});
|
||||
|
||||
test('Rendered no "email" and Support EmailRecipient ', function() {
|
||||
|
||||
contact.email = null;
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var result = renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
assert.ok(result);
|
||||
});
|
||||
|
||||
test('Rendered no "Tel"/"email" and Support EmailRecipient ', function() {
|
||||
|
||||
contact.tel = null;
|
||||
contact.email = null;
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var result = renderer.render({
|
||||
contact: contact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
assert.isFalse(result);
|
||||
});
|
||||
});
|
||||
|
||||
suite('suggestionUnknown', function() {
|
||||
var html;
|
||||
var unknownContact = {
|
||||
name: ['unknown'],
|
||||
tel: [{value: '+346578888888'}]
|
||||
};
|
||||
setup(function() {
|
||||
unknownRender = ContactRenderer.flavor('suggestionUnknown');
|
||||
});
|
||||
|
||||
test('Rendered unknownContact "number"', function() {
|
||||
|
||||
unknownContact.tel[0].carrier = null;
|
||||
unknownContact.tel[0].type = null;
|
||||
|
||||
unknownRender.render({
|
||||
contact: unknownContact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
|
||||
assert.ok(html.includes('+346578888888'));
|
||||
});
|
||||
|
||||
test('Rendered unknownContact highlighted "number"', function() {
|
||||
|
||||
unknownContact.tel[0].carrier = null;
|
||||
unknownContact.tel[0].type = null;
|
||||
|
||||
unknownRender.render({
|
||||
contact: unknownContact,
|
||||
input: '346578888888',
|
||||
target: ul
|
||||
});
|
||||
|
||||
sinon.assert.calledWithMatch(Template.prototype.interpolate, {
|
||||
carrier: '',
|
||||
name: 'unknown',
|
||||
nameHTML: 'unknown',
|
||||
number: '+346578888888',
|
||||
phoneDetailsHTML: SharedComponents.phoneDetails(
|
||||
{ number: '+<span class="highlight">346578888888</span>' },
|
||||
{ safe: ['number'] }
|
||||
).toString(),
|
||||
photoHTML: '',
|
||||
type: ''
|
||||
});
|
||||
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
|
||||
assert.ok(
|
||||
html.includes('+<span class="highlight">346578888888</span>')
|
||||
);
|
||||
});
|
||||
|
||||
test('Rendered unknownContact omit numbers already in recipient list',
|
||||
function() {
|
||||
|
||||
var skip = ['+346578888888'];
|
||||
|
||||
// This unknownContact has two tel entries.
|
||||
unknownRender.render({
|
||||
contact: unknownContact,
|
||||
input: '+346578888888',
|
||||
target: ul,
|
||||
skip: skip
|
||||
});
|
||||
|
||||
html = ul.innerHTML;
|
||||
|
||||
assert.ok(!html.includes('346578888888'));
|
||||
assert.equal(ul.children.length, 0);
|
||||
});
|
||||
|
||||
test('does not include photo', function() {
|
||||
|
||||
unknownRender.render({
|
||||
contact: unknownContact,
|
||||
input: 'foo',
|
||||
target: ul
|
||||
});
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
|
||||
assert.isFalse(html.includes('span[data-type=img]'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
suite('prompt', function() {
|
||||
setup(function() {
|
||||
renderer = ContactRenderer.flavor('prompt');
|
||||
});
|
||||
|
||||
test('Rendered Contact w/ multiple: one', function() {
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '+12125559999',
|
||||
target: ul
|
||||
});
|
||||
|
||||
assert.equal(ul.children.length, 1);
|
||||
});
|
||||
|
||||
test('Rendered Contact w/ multiple: one w/ minimal match', function() {
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '5559999',
|
||||
target: ul
|
||||
});
|
||||
|
||||
assert.equal(ul.children.length, 1);
|
||||
});
|
||||
|
||||
test('Render contact without photo does not render the photo', function() {
|
||||
var html;
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '5559999',
|
||||
target: ul
|
||||
});
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
|
||||
assert.isFalse(html.includes('span[data-type=img]'));
|
||||
});
|
||||
|
||||
test('Render contact with photo renders the image', function() {
|
||||
var html;
|
||||
var blob = testImageBlob;
|
||||
this.sinon.stub(MockContactPhotoHelper, 'getThumbnail').returns(blob);
|
||||
this.sinon.spy(Utils, 'asyncLoadRevokeURL');
|
||||
this.sinon.spy(Utils, 'getContactDetails');
|
||||
this.sinon.spy(window, 'encodeURI');
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
input: '5559999',
|
||||
target: ul
|
||||
});
|
||||
|
||||
sinon.assert.calledWith(Template.prototype.interpolate, undefined);
|
||||
|
||||
var photo = 'data-type="img"';
|
||||
sinon.assert.calledWithMatch(Template.prototype.interpolate, {
|
||||
carrier: 'XXX',
|
||||
name: 'Pepito O\'Hare',
|
||||
nameHTML: 'Pepito O'Hare',
|
||||
number: '+12125559999',
|
||||
phoneDetailsHTML: SharedComponents.phoneDetails(
|
||||
{
|
||||
number: '+12125559999',
|
||||
type: 'B',
|
||||
carrier: 'XXX'
|
||||
},
|
||||
{ safe: ['number'] }
|
||||
).toString(),
|
||||
photoHTML: sinon.match(photo),
|
||||
type: 'B'
|
||||
});
|
||||
|
||||
html = ul.firstElementChild.innerHTML;
|
||||
var contactPhotoElement = ul.firstElementChild.querySelector(
|
||||
'.contact-photo'
|
||||
);
|
||||
|
||||
assert.ok(html.includes('span'));
|
||||
assert.ok(contactPhotoElement.style.backgroundImage.indexOf('blob:') > 0);
|
||||
sinon.assert.calledWith(
|
||||
encodeURI,
|
||||
Utils.getContactDetails.returnValues[0].photoURL
|
||||
);
|
||||
sinon.assert.calledWith(
|
||||
Utils.asyncLoadRevokeURL,
|
||||
Utils.getContactDetails.returnValues[0].photoURL
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Secure User Input', function() {
|
||||
setup(function() {
|
||||
this.sinon.stub(Utils, 'getContactDetails', function(number, contacts) {
|
||||
return {
|
||||
isContact: !!contacts,
|
||||
title: number
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
test('+99', function() {
|
||||
var ul = document.createElement('ul');
|
||||
|
||||
assert.doesNotThrow(function() {
|
||||
var renderer = ContactRenderer.flavor('suggestion');
|
||||
|
||||
renderer.render({
|
||||
contact: {
|
||||
name: 'Spider Monkey',
|
||||
tel: [{ value: '...' }]
|
||||
},
|
||||
input: '+99',
|
||||
target: ul
|
||||
});
|
||||
});
|
||||
|
||||
sinon.assert.calledWith(Utils.getContactDetails, '...');
|
||||
});
|
||||
|
||||
test('*67 [800]-555-1212', function() {
|
||||
var ul = document.createElement('ul');
|
||||
|
||||
assert.doesNotThrow(function() {
|
||||
var renderer = ContactRenderer.flavor('suggestion');
|
||||
renderer.render({
|
||||
contact: {
|
||||
name: 'Spider Monkey',
|
||||
tel: [{ value: '...' }]
|
||||
},
|
||||
input: '*67 [800]-555-1212',
|
||||
target: ul
|
||||
});
|
||||
});
|
||||
|
||||
sinon.assert.calledWith(Utils.getContactDetails, '...');
|
||||
});
|
||||
|
||||
test('\\^$*+?.', function() {
|
||||
var ul = document.createElement('ul');
|
||||
assert.doesNotThrow(function() {
|
||||
var renderer = ContactRenderer.flavor('suggestion');
|
||||
renderer.render({
|
||||
contact: {
|
||||
name: 'Spider Monkey',
|
||||
tel: [{ value: '...' }]
|
||||
},
|
||||
input: '\\^$*+?.',
|
||||
target: ul
|
||||
});
|
||||
});
|
||||
sinon.assert.calledWith(Utils.getContactDetails, '...');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Defensive Contact Rendering', function() {
|
||||
test('has tel number', function() {
|
||||
var ul = document.createElement('ul');
|
||||
var contact = new MockContact();
|
||||
|
||||
var renderer = ContactRenderer.flavor('suggestion');
|
||||
var isRendered = renderer.render({
|
||||
contact: contact,
|
||||
input: contact.tel[0].value,
|
||||
target: ul
|
||||
});
|
||||
|
||||
assert.isTrue(isRendered);
|
||||
});
|
||||
|
||||
test('no tel number, has email address', function() {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var ul = document.createElement('ul');
|
||||
var contact = new MockContact();
|
||||
contact.tel = null;
|
||||
|
||||
var renderer = ContactRenderer.flavor('suggestion');
|
||||
var isRendered = renderer.render({
|
||||
contact: contact,
|
||||
input: contact.email[0].value,
|
||||
target: ul
|
||||
});
|
||||
|
||||
assert.isTrue(isRendered);
|
||||
});
|
||||
|
||||
test('has tel number, no email address', function() {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var ul = document.createElement('ul');
|
||||
var contact = new MockContact();
|
||||
contact.email = null;
|
||||
|
||||
var renderer = ContactRenderer.flavor('suggestion');
|
||||
var isRendered = renderer.render({
|
||||
contact: contact,
|
||||
input: contact.tel[0].value,
|
||||
target: ul
|
||||
});
|
||||
|
||||
assert.isTrue(isRendered);
|
||||
});
|
||||
|
||||
test('no tel number, no email address', function() {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var ul = document.createElement('ul');
|
||||
var contact = new MockContact();
|
||||
contact.tel = null;
|
||||
contact.email = null;
|
||||
|
||||
var renderer = ContactRenderer.flavor('suggestion');
|
||||
var isNotRendered = renderer.render({
|
||||
contact: contact,
|
||||
input: null,
|
||||
target: ul
|
||||
});
|
||||
|
||||
assert.isFalse(isNotRendered);
|
||||
});
|
||||
});
|
||||
|
||||
suite('report-view', function() {
|
||||
var renderer;
|
||||
|
||||
setup(function() {
|
||||
renderer = ContactRenderer.flavor('report-view');
|
||||
});
|
||||
|
||||
test('correct rendering', function() {
|
||||
var selector = '.js-contact-info';
|
||||
var infoBlock = document.createElement('div');
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
target: ul,
|
||||
input: contact.tel[0].value,
|
||||
infoBlock: infoBlock,
|
||||
infoBlockParentSelector: selector,
|
||||
});
|
||||
|
||||
var contactInfo = ul.querySelector(selector);
|
||||
assert.isTrue(contactInfo.contains(infoBlock));
|
||||
assert.notEqual(contactInfo.tagName, 'A');
|
||||
});
|
||||
});
|
||||
|
||||
suite('group-view', function() {
|
||||
var renderer;
|
||||
|
||||
setup(function() {
|
||||
renderer = ContactRenderer.flavor('group-view');
|
||||
});
|
||||
|
||||
test('correct rendering', function() {
|
||||
var selector = '.js-contact-info';
|
||||
|
||||
renderer.render({
|
||||
contact: contact,
|
||||
target: ul,
|
||||
input: contact.tel[0].value
|
||||
});
|
||||
|
||||
var contactInfo = ul.querySelector(selector);
|
||||
assert.equal(contactInfo.tagName, 'A');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
890
gaia/apps/sms/views/shared/test/unit/contacts_test.js
Executable file
890
gaia/apps/sms/views/shared/test/unit/contacts_test.js
Executable file
|
|
@ -0,0 +1,890 @@
|
|||
/*global MockContact, Contacts, fb, MockFbReaderUtilsObj,
|
||||
MockSettings, MocksHelper */
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_moz_phone_number_service.js');
|
||||
require('/shared/test/unit/mocks/mock_fb_reader_utils.js');
|
||||
require('/views/shared/js/utils.js');
|
||||
require('/views/shared/test/unit/mock_contact.js');
|
||||
require('/views/shared/js/contacts.js');
|
||||
require('/views/shared/test/unit/mock_settings.js');
|
||||
require('/views/shared/test/unit/mock_utils.js');
|
||||
|
||||
var mocksHelperForContactsUnitTest = new MocksHelper([
|
||||
'Settings',
|
||||
'Utils'
|
||||
]).init();
|
||||
|
||||
function withFilter(str) {
|
||||
return sinon.match( { filterValue: str } );
|
||||
}
|
||||
|
||||
suite('Contacts', function(done) {
|
||||
mocksHelperForContactsUnitTest.attachTestHelpers();
|
||||
var realFb = window.fb;
|
||||
|
||||
var targetFbNumber = '+34658789147';
|
||||
var fbContactName = 'Carlos Facebook';
|
||||
var targetLocalNumber = '+34698745123';
|
||||
var targetLocalEmail = 'a@b.com';
|
||||
var localContactName = 'Jose Local';
|
||||
var notFoundNumber = '+34633789102';
|
||||
var notFoundEmail = 'a@c.com';
|
||||
|
||||
suiteSetup(function() {
|
||||
var mockReaderUtils = new MockFbReaderUtilsObj();
|
||||
mockReaderUtils.targetFbNumber = targetFbNumber;
|
||||
mockReaderUtils.fbContactName = fbContactName;
|
||||
window.fb = mockReaderUtils;
|
||||
});
|
||||
|
||||
setup(function(){
|
||||
// Do not use the Native API
|
||||
this.sinon.stub(navigator.mozContacts, 'find');
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
window.fb = realFb;
|
||||
});
|
||||
|
||||
suite('Contacts.findContactByString, single-term', function() {
|
||||
|
||||
test('(string[tel,givenName,familyName], ...) Match', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('O\'Hare')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('O\'Hare').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('(string[tel,givenName,familyName], ...) No Match', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('wontmatch')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findContactByString('wontmatch').then(function(contacts) {
|
||||
// contacts were not found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('(string[tel,email,givenName,familyName], ...) Match', function(done) {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('a@b.com')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('a@b.com').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('(string[tel,email,givenName,familyName], ...) No Match',
|
||||
function(done) {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('z@y.com')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findContactByString('z@y.com').then(function(contacts) {
|
||||
// contacts were not found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('(string[tel], ...) Match', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('+346578888888')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('+346578888888').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('(string[tel], ...) No Match', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('911')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findContactByString('911').then(function(contacts) {
|
||||
// contacts were not found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Contacts.findContactByString, multi-term', function() {
|
||||
|
||||
test('no predominate', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('Pepito')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('Pepito O\'Hare').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('no predominate, reversed', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('O\'Hare')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('O\'Hare Pepito').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('predominate first, upper', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('Pepi')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('Pepi O').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('predominate last, upper', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('Pepi')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('O Pepi').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
|
||||
test('predominate first, lower', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('pepi')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('pepi o').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('predominate last, lower', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('pepi')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('o pepi').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('multi-word name', function(done) {
|
||||
var result = MockContact.list([
|
||||
{ givenName: ['Mary Anne'], familyName: ['Jones'] }
|
||||
]);
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('mary')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('mary anne').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('name first, part of tel number last', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('Pepito')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('Pepito 8888').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('part of tel number first, name last', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('Pepito')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('8888 Pepito').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('name first, part of email address last', function(done) {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('Pepito')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findByString('Pepito a@b').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('part of email address first, name last', function(done) {
|
||||
MockSettings.supportEmailRecipient = true;
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('Pepito')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findByString('a@b Pepito').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('string search yields a contact without familyName', function(done) {
|
||||
var result = MockContact.list([
|
||||
{ givenName: ['julien'] }
|
||||
]);
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('julien')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('julien 123').then((contacts) => {
|
||||
// "julien" yields a result that has only a givenName, no familyName.
|
||||
// This test checks that our algorithm works also in such cases.
|
||||
// See Bug 952533
|
||||
assert.isNotNull(contacts);
|
||||
assert.lengthOf(contacts, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('no matches, predominate first, upper', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('Pepito')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findContactByString('Pepito S').then(function(contacts) {
|
||||
// No contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('no matches, predominate last, upper', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('Pepito')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findContactByString('S Pepito').then(function(contacts) {
|
||||
// No contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('no matches, predominate first, lower', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('pepi')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findContactByString('pepi s').then(function(contacts) {
|
||||
// No contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('no matches, predominate last, lower', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('pepi')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findContactByString('s pepi').then(function(contacts) {
|
||||
// No contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
suite('Contacts.findExact', function() {
|
||||
|
||||
test('yields a match ', function(done) {
|
||||
var result = MockContact.list([
|
||||
{ givenName: ['Jane'], familyName: ['Doozer'] }
|
||||
]);
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('doozer')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findExact('jane doozer').then(function(contacts) {
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('yields no matches ', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('doozer')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findExact('j doozer').then(function(contacts) {
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Contacts.findByPhoneNumber', function() {
|
||||
|
||||
test('removes spaces', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('+33123456789')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findByPhoneNumber('+33 1 23 45 67 89').then(function(contacts){
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('The mozContacts find() call returned an error', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('callonerror')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findByPhoneNumber('callonerror').then((results) => {
|
||||
assert.isArray(results);
|
||||
assert.lengthOf(results, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local number found.', function(done) {
|
||||
var result = [{
|
||||
name: [localContactName]
|
||||
}];
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(targetLocalNumber)
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findByPhoneNumber(targetLocalNumber).then(function(contacts) {
|
||||
assert.equal(contacts.length, 1);
|
||||
assert.isTrue(!contacts[0].isFbContact);
|
||||
assert.equal(contacts[0].name[0], localContactName);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local number not found. FB Number found', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(targetFbNumber)
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findByPhoneNumber(targetFbNumber).then(function(contacts) {
|
||||
assert.equal(contacts.length, 1);
|
||||
assert.equal(contacts[0].name[0], fbContactName);
|
||||
assert.equal(contacts[0].isFbContact, true);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local number not found. FB Number not found either', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(notFoundNumber)
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findByPhoneNumber(notFoundNumber).then(function(contacts) {
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local number not found. FB returns error', function(done) {
|
||||
fb.inError = true;
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(targetFbNumber)
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findByPhoneNumber(targetFbNumber).then(function(contacts) {
|
||||
assert.equal(contacts.length, 0);
|
||||
delete window.fb.inError;
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Contacts.findByAddress', function() {
|
||||
|
||||
test('removes spaces', function(done) {
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('+33123456789')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findByAddress('+33 1 23 45 67 89').then(function(contacts){
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('The mozContacts find() call returned an error', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('callonerror')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findByAddress('callonerror').then((results) => {
|
||||
assert.isArray(results);
|
||||
assert.lengthOf(results, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local number found.', function(done) {
|
||||
var result = [{
|
||||
name: [localContactName]
|
||||
}];
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(targetLocalNumber)
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findByAddress(targetLocalNumber).then(function(contacts) {
|
||||
assert.equal(contacts.length, 1);
|
||||
assert.isTrue(!contacts[0].isFbContact);
|
||||
assert.equal(contacts[0].name[0], localContactName);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local email found.', function(done) {
|
||||
var result = [{
|
||||
name: [localContactName]
|
||||
}];
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(targetLocalEmail)
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findByAddress(targetLocalEmail).then(function(contacts) {
|
||||
assert.equal(contacts.length, 1);
|
||||
assert.isTrue(!contacts[0].isFbContact);
|
||||
assert.equal(contacts[0].name[0], localContactName);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local number not found. FB Number found', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(targetFbNumber)
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findByAddress(targetFbNumber).then(function(contacts) {
|
||||
assert.equal(contacts.length, 1);
|
||||
assert.equal(contacts[0].name[0], fbContactName);
|
||||
assert.equal(contacts[0].isFbContact, true);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local number not found. FB Number not found either', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(notFoundNumber)
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findByAddress(notFoundNumber).then(function(contacts) {
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local email not found. FB Number not found either', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(notFoundEmail)
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findByAddress(notFoundEmail).then(function(contacts) {
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local number not found. FB returns error', function(done) {
|
||||
fb.inError = true;
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(targetFbNumber)
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findByAddress(targetFbNumber).then(function(contacts) {
|
||||
assert.equal(contacts.length, 0);
|
||||
delete window.fb.inError;
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Contacts.findExactByEmail', function() {
|
||||
|
||||
test('Local email found.', function(done) {
|
||||
var result = [{
|
||||
name: [localContactName]
|
||||
}];
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(targetLocalEmail)
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findExactByEmail(targetLocalEmail).then(function(contacts) {
|
||||
assert.equal(contacts.length, 1);
|
||||
assert.isTrue(!contacts[0].isFbContact);
|
||||
assert.equal(contacts[0].name[0], localContactName);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local email not found. FB Email not found either', function(done) {
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(notFoundEmail)
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findExactByEmail(notFoundEmail).then(function(contacts) {
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Local email not found. FB returns error', function(done) {
|
||||
fb.inError = true;
|
||||
var result = [{
|
||||
name: [localContactName]
|
||||
}];
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter(targetLocalEmail)
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findExactByEmail(targetLocalEmail).then(function(contacts) {
|
||||
assert.equal(contacts.length, 1);
|
||||
delete window.fb.inError;
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Contacts.addUnknown', function() {
|
||||
|
||||
test('For adding an unknown contact', function() {
|
||||
var length = Contacts.getunknownLength();
|
||||
Contacts.addUnknown('123456');
|
||||
|
||||
assert.equal(length + 1, Contacts.getunknownLength());
|
||||
});
|
||||
|
||||
test('For not adding the duplicate unknown contact', function() {
|
||||
var length = Contacts.getunknownLength();
|
||||
|
||||
Contacts.addUnknown('123456');
|
||||
assert.equal(length, Contacts.getunknownLength());
|
||||
});
|
||||
});
|
||||
|
||||
suite('Contacts.clearUnknown', function() {
|
||||
|
||||
test('For clearing unknown contact', function() {
|
||||
Contacts.clearUnknown();
|
||||
var length = Contacts.getunknownLength();
|
||||
|
||||
assert.equal(length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Contacts.findByUnknown function', function() {
|
||||
|
||||
setup(function() {
|
||||
Contacts.clearUnknown();
|
||||
Contacts.addUnknown('123123456');
|
||||
Contacts.addUnknown('789789789');
|
||||
Contacts.addUnknown('456456456');
|
||||
Contacts.addUnknown('123456789');
|
||||
});
|
||||
|
||||
test('checking single unknown contact using substring', function(done) {
|
||||
|
||||
Contacts.findByUnknown('6456').then(function(list) {
|
||||
assert.equal(list[0].name == '456456456', true);
|
||||
assert.equal(list.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('checking multiple unknown contact using substring', function(done) {
|
||||
|
||||
Contacts.findByUnknown('456').then(function(list) {
|
||||
assert.equal(list[0].name == '123123456', true);
|
||||
assert.equal(list[1].name == '456456456', true);
|
||||
assert.equal(list[2].name == '123456789', true);
|
||||
assert.equal(list.length, 3);//List can have at max 3 unknown elements
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('checking multiple unknown contact using fullstring', function(done) {
|
||||
|
||||
Contacts.findByUnknown('456456456').then(function(list) {
|
||||
assert.equal(list[0].name == '456456456', true);
|
||||
assert.equal(list.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('checking unknown contact after clearUnknown', function(done) {
|
||||
Contacts.clearUnknown();
|
||||
|
||||
Contacts.findByUnknown('456').then(function(list) {
|
||||
assert.equal(list.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Unknown List cannot have more than 3 prediction', function(done) {
|
||||
Contacts.clearUnknown();
|
||||
Contacts.addUnknown('123123456');
|
||||
Contacts.addUnknown('789123789');
|
||||
Contacts.addUnknown('456456123');
|
||||
Contacts.addUnknown('123456789');
|
||||
|
||||
Contacts.findByUnknown('123').then(function(list) {
|
||||
//Prediction should had been 4 but since we restict it to a max of 3
|
||||
assert.equal(list.length, 3);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('Contacts.findBy (success)', function() {
|
||||
|
||||
test('(object, ...), Match', function(done) {
|
||||
var filter = {
|
||||
filterBy: ['tel'],
|
||||
filterOp: 'contains',
|
||||
filterValue: '12125559999'
|
||||
};
|
||||
var result = MockContact.list();
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('12125559999')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findBy(filter).then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 1);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('(object, ...), No Match', function(done) {
|
||||
var filter = {
|
||||
filterBy: ['tel'],
|
||||
filterOp: 'contains',
|
||||
filterValue: '911'
|
||||
};
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('911')
|
||||
).returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
|
||||
Contacts.findBy(filter).then(function(contacts) {
|
||||
// contacts were not found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Contacts.findBy (error)', function() {
|
||||
test('({}, ...)', function(done) {
|
||||
Contacts.findBy({}).then(function(contacts) {
|
||||
assert.isTrue(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 0);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Contacts validation', function() {
|
||||
|
||||
test('Contact validation, predominate first', function(done) {
|
||||
var result = MockContact.list([
|
||||
{ givenName: ['Jane'], familyName: ['D'] },
|
||||
|
||||
{ givenName: ['jane'], familyName: ['doe'] },
|
||||
|
||||
{ givenName: ['jane'], familyName: ['dow'] },
|
||||
|
||||
{ givenName: ['Jane'], familyName: ['Doozer'] }
|
||||
]);
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('jane')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('jane d').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
// This was relaxed by the change from "startsWith" to "contains"
|
||||
assert.equal(contacts.length, 4);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Contact validation, predominate last', function(done) {
|
||||
var result = MockContact.list([
|
||||
{ givenName: ['Jane'], familyName: ['Doozer'] },
|
||||
|
||||
{ givenName: ['jane'], familyName: ['doe'] },
|
||||
|
||||
{ givenName: ['jerry'], familyName: ['doe'] },
|
||||
|
||||
{ givenName: ['j'], familyName: ['dow'] },
|
||||
|
||||
{ givenName: ['john'], familyName: ['doland'] }
|
||||
]);
|
||||
|
||||
navigator.mozContacts.find.withArgs(
|
||||
withFilter('do')
|
||||
).returns(
|
||||
Promise.resolve(result)
|
||||
);
|
||||
|
||||
Contacts.findContactByString('j do').then(function(contacts) {
|
||||
// contacts were found
|
||||
assert.ok(Array.isArray(contacts));
|
||||
assert.equal(contacts.length, 5);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
288
gaia/apps/sms/views/shared/test/unit/dialog_test.js
Executable file
288
gaia/apps/sms/views/shared/test/unit/dialog_test.js
Executable file
|
|
@ -0,0 +1,288 @@
|
|||
/*global loadBodyHTML,
|
||||
Dialog,
|
||||
MockL10n,
|
||||
TransitionEvent
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l20n.js');
|
||||
|
||||
require('/views/shared/js/dialog.js');
|
||||
|
||||
suite('Dialog', function() {
|
||||
var nativeMozL10n = document.l10n;
|
||||
var params = null;
|
||||
|
||||
suiteSetup(function() {
|
||||
loadBodyHTML('/index.html');
|
||||
document.l10n = MockL10n;
|
||||
params = {
|
||||
title: {
|
||||
raw: 'Foo Title'
|
||||
},
|
||||
body: {
|
||||
raw: 'Foo Body'
|
||||
},
|
||||
options: {
|
||||
cancel: {
|
||||
text: {
|
||||
raw: 'Foo Cancel'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
document.l10n = nativeMozL10n;
|
||||
params = null;
|
||||
});
|
||||
|
||||
test('Creation', function() {
|
||||
var dialog = new Dialog(params);
|
||||
// We check if the object is created properly
|
||||
assert.isFalse(!dialog.show);
|
||||
assert.equal(typeof dialog.show, 'function');
|
||||
assert.isFalse(!dialog.hide);
|
||||
assert.equal(typeof dialog.hide, 'function');
|
||||
});
|
||||
|
||||
test('Appending to DOM', function() {
|
||||
var previouslyDefinedForms = document.getElementsByTagName('form').length;
|
||||
// In this case we have several forms pre-defined (5):
|
||||
// - "messages-compose-form"
|
||||
// - "messages-edit-form"
|
||||
// - "loading"
|
||||
// - "threads-edit-form"
|
||||
assert.equal(previouslyDefinedForms, 4);
|
||||
// Now we create the new element
|
||||
var dialog = new Dialog(params);
|
||||
// We check if the object is appended to the DOM
|
||||
dialog.show();
|
||||
// Is appended properly?
|
||||
var currentlyDefinedForms = document.getElementsByTagName('form');
|
||||
var currentlyDefinedFormsLength = currentlyDefinedForms.length;
|
||||
assert.equal(currentlyDefinedFormsLength, 5);
|
||||
// We check the type
|
||||
var dialogForm = currentlyDefinedForms[currentlyDefinedFormsLength - 1];
|
||||
assert.equal(dialogForm.dataset.type, 'confirm');
|
||||
});
|
||||
|
||||
test('Has GaiaSimPicker', function() {
|
||||
assert.ok(document.getElementById('sim-picker'));
|
||||
});
|
||||
|
||||
test('Focus', function() {
|
||||
var dialog = new Dialog(params);
|
||||
|
||||
this.sinon.stub(dialog.form, 'focus');
|
||||
this.sinon.stub(HTMLElement.prototype, 'blur');
|
||||
|
||||
dialog.show();
|
||||
|
||||
sinon.assert.called(HTMLElement.prototype.blur);
|
||||
|
||||
var transitionend = new TransitionEvent('transitionend', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
propertyName: 'transform'
|
||||
});
|
||||
dialog.form.dispatchEvent(transitionend);
|
||||
|
||||
sinon.assert.called(dialog.form.focus);
|
||||
});
|
||||
|
||||
test('Redundant shows have no effect', function() {
|
||||
var dialog = new Dialog(params);
|
||||
|
||||
var spy = this.sinon.spy(document.body, 'appendChild');
|
||||
|
||||
dialog.show();
|
||||
dialog.show();
|
||||
|
||||
assert.ok(spy.calledOnce);
|
||||
});
|
||||
|
||||
test('Hiding removes element after transition', function() {
|
||||
var dialog = new Dialog(params);
|
||||
|
||||
dialog.show();
|
||||
assert.notEqual(dialog.form.parentElement, null);
|
||||
|
||||
dialog.hide();
|
||||
assert.notEqual(dialog.form.parentElement, null);
|
||||
|
||||
var transitionend = new TransitionEvent('transitionend', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
propertyName: 'transform'
|
||||
});
|
||||
dialog.form.dispatchEvent(transitionend);
|
||||
assert.equal(dialog.form.parentElement, null);
|
||||
});
|
||||
|
||||
test('Checking the structure. Default.', function() {
|
||||
// Now we create the new element
|
||||
var dialog = new Dialog(params);
|
||||
// We append the element to the DOM
|
||||
dialog.show();
|
||||
// We retrieve the last created form
|
||||
var currentlyDefinedForms = document.getElementsByTagName('form');
|
||||
var currentlyDefinedFormsLength = currentlyDefinedForms.length;
|
||||
// We check the type
|
||||
var dialogForm = currentlyDefinedForms[currentlyDefinedFormsLength - 1];
|
||||
// We check how many buttons we have (only the mandatory one)
|
||||
var formOptions = dialogForm.getElementsByTagName('button');
|
||||
assert.equal(formOptions.length, 1);
|
||||
});
|
||||
|
||||
test('Checking the structure. Confirm.', function() {
|
||||
// We add the confirm
|
||||
params.options.confirm = {
|
||||
text: {
|
||||
raw: 'Foo Cancel'
|
||||
}
|
||||
};
|
||||
// Now we create the new element
|
||||
var dialog = new Dialog(params);
|
||||
// We append the element to the DOM
|
||||
dialog.show();
|
||||
// We retrieve the last created form
|
||||
var currentlyDefinedForms = document.getElementsByTagName('form');
|
||||
var currentlyDefinedFormsLength = currentlyDefinedForms.length;
|
||||
// We check the type
|
||||
var dialogForm = currentlyDefinedForms[currentlyDefinedFormsLength - 1];
|
||||
// We check how many buttons we have (mandatory + confirm one)
|
||||
var formOptions = dialogForm.getElementsByTagName('button');
|
||||
assert.equal(formOptions.length, 2);
|
||||
// We check if there is a 'recommend' style
|
||||
var optionalOptions = dialogForm.getElementsByClassName('recommend');
|
||||
assert.equal(optionalOptions.length, 1);
|
||||
});
|
||||
|
||||
test('Checking the structure. Confirm (with custom class name).', function() {
|
||||
// We add the confirm
|
||||
params.options.confirm = {
|
||||
text: {
|
||||
raw: 'Foo Cancel'
|
||||
},
|
||||
className: 'test-class'
|
||||
};
|
||||
// Now we create the new element
|
||||
var dialog = new Dialog(params);
|
||||
// We append the element to the DOM
|
||||
dialog.show();
|
||||
// We retrieve the last created form
|
||||
var currentlyDefinedForms = document.getElementsByTagName('form');
|
||||
var currentlyDefinedFormsLength = currentlyDefinedForms.length;
|
||||
// We check the type
|
||||
var dialogForm = currentlyDefinedForms[currentlyDefinedFormsLength - 1];
|
||||
|
||||
// We check whether default 'recommend' class wasn't applied
|
||||
var optionalOptions = dialogForm.getElementsByClassName('recommend');
|
||||
assert.equal(optionalOptions.length, 0);
|
||||
// We check whether custom class name was applied
|
||||
optionalOptions = dialogForm.getElementsByClassName('test-class');
|
||||
assert.equal(optionalOptions.length, 1);
|
||||
});
|
||||
|
||||
test('Checking the localization.', function() {
|
||||
params.title = 'l10n Title',
|
||||
params.body = { raw: 'non-l10n Body' },
|
||||
params.options.cancel = {
|
||||
text: { id: 'l10n keyCancel' }
|
||||
};
|
||||
params.options.confirm = {
|
||||
text: { id: 'l10n keyConfirm', args: { n: 1 } }
|
||||
};
|
||||
var l10nSpy = this.sinon.spy(document.l10n, 'setAttributes');
|
||||
// Now we create the new element
|
||||
var dialog = new Dialog(params);
|
||||
// We append the element to the DOM
|
||||
dialog.show();
|
||||
// We retrieve the last created form
|
||||
var currentlyDefinedForms = document.getElementsByTagName('form');
|
||||
var currentlyDefinedFormsLength = currentlyDefinedForms.length;
|
||||
// We check the type
|
||||
var dialogForm = currentlyDefinedForms[currentlyDefinedFormsLength - 1];
|
||||
// We check how many buttons we have (mandatory + confirm one)
|
||||
var titleDOM = dialogForm.querySelector('h1');
|
||||
var bodyDOM = dialogForm.querySelector('p');
|
||||
var formOptions = dialogForm.getElementsByTagName('button');
|
||||
assert.equal(formOptions.length, 2);
|
||||
|
||||
// We check localization
|
||||
assert.equal(titleDOM.getAttribute('data-l10n-id'), params.title);
|
||||
assert.equal(bodyDOM.textContent, params.body.raw);
|
||||
assert.isFalse(bodyDOM.hasAttribute('data-l10n-id'));
|
||||
sinon.assert.calledWith(
|
||||
l10nSpy, formOptions[0], params.options.cancel.text.id
|
||||
);
|
||||
sinon.assert.calledWith(
|
||||
l10nSpy, formOptions[1],
|
||||
params.options.confirm.text.id, params.options.confirm.text.args
|
||||
);
|
||||
});
|
||||
|
||||
test('Checking parametrized body localization.', function() {
|
||||
params.title = 'l10n Title';
|
||||
params.body = {
|
||||
id: 'l10n Body',
|
||||
args: {
|
||||
n: 3,
|
||||
numbers: ['123', '456', '789']
|
||||
}
|
||||
};
|
||||
params.options.cancel = {
|
||||
text: 'l10n keyCancel'
|
||||
};
|
||||
params.options.confirm = {
|
||||
text: 'l10n keyConfirm'
|
||||
};
|
||||
|
||||
// Now we create the new element
|
||||
var dialog = new Dialog(params);
|
||||
// We append the element to the DOM
|
||||
dialog.show();
|
||||
// We retrieve the last created form
|
||||
var currentlyDefinedForms = document.getElementsByTagName('form');
|
||||
var currentlyDefinedFormsLength = currentlyDefinedForms.length;
|
||||
// We check the type
|
||||
var dialogForm = currentlyDefinedForms[currentlyDefinedFormsLength - 1];
|
||||
// We check how many buttons we have (mandatory + confirm one)
|
||||
var bodyDOM = dialogForm.querySelector('p');
|
||||
// We check localization
|
||||
assert.equal(
|
||||
bodyDOM.getAttribute('data-l10n-id'), params.body.id,
|
||||
'Body DOM localized with proper string'
|
||||
);
|
||||
assert.equal(
|
||||
bodyDOM.getAttribute('data-l10n-args'), JSON.stringify(params.body.args),
|
||||
'Body DOM localized with proper string'
|
||||
);
|
||||
});
|
||||
|
||||
test('Should prevent pointer events before transitionend', function() {
|
||||
// Now we create the new element
|
||||
var dialog = new Dialog(params);
|
||||
// We append the element to the DOM
|
||||
dialog.show();
|
||||
assert.isTrue(document.body.classList.contains('dialog-animating'));
|
||||
var transitionend = new TransitionEvent('transitionend', {
|
||||
bubbles: true,
|
||||
propertyName: 'transform'
|
||||
});
|
||||
dialog.form.dispatchEvent(transitionend);
|
||||
assert.isFalse(document.body.classList.contains('dialog-animating'));
|
||||
});
|
||||
|
||||
test('adding a class name', function() {
|
||||
params.classes = ['specific-class1', 'specific-class2'];
|
||||
var dialog = new Dialog(params);
|
||||
dialog.show();
|
||||
|
||||
var elt = document.querySelector('.specific-class1');
|
||||
assert.ok(elt);
|
||||
assert.ok(elt.classList.contains('specific-class2'));
|
||||
});
|
||||
});
|
||||
306
gaia/apps/sms/views/shared/test/unit/error_dialog_test.js
Executable file
306
gaia/apps/sms/views/shared/test/unit/error_dialog_test.js
Executable file
|
|
@ -0,0 +1,306 @@
|
|||
/*global Dialog,
|
||||
ErrorDialog,
|
||||
MockL10n,
|
||||
MocksHelper,
|
||||
Settings
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l20n.js');
|
||||
|
||||
require('/views/shared/test/unit/mock_dialog.js');
|
||||
require('/views/shared/test/unit/mock_settings.js');
|
||||
|
||||
require('/views/shared/js/dialog.js');
|
||||
require('/views/shared/js/error_dialog.js');
|
||||
|
||||
var mocksHelperForDialog = new MocksHelper([
|
||||
'Dialog',
|
||||
'Settings'
|
||||
]).init();
|
||||
|
||||
suite('ErrorDialog', function() {
|
||||
// Silencing jshint complaints related to "new ErrorDialog(..)" invocations
|
||||
/*jshint nonew: false */
|
||||
var realL10n;
|
||||
|
||||
mocksHelperForDialog.attachTestHelpers();
|
||||
|
||||
setup(function() {
|
||||
realL10n = document.l10n;
|
||||
document.l10n = MockL10n;
|
||||
this.sinon.spy(Dialog, 'call');
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
document.l10n = realL10n;
|
||||
});
|
||||
|
||||
suite('basic tests', function() {
|
||||
test('uses correct Dialog title, body and cancel button l10nIds',
|
||||
function() {
|
||||
var errorDescription = {
|
||||
prefix: 'TEST'
|
||||
};
|
||||
|
||||
(new ErrorDialog(errorDescription));
|
||||
|
||||
sinon.assert.calledWith(Dialog.call, sinon.match.any, {
|
||||
title: errorDescription.prefix + 'Title',
|
||||
body: errorDescription.prefix + 'Body',
|
||||
options: {
|
||||
cancel: {
|
||||
text: errorDescription.prefix + 'BtnOk'
|
||||
}
|
||||
},
|
||||
classes: ['error-dialog-' + errorDescription.prefix, 'error-dialog']
|
||||
});
|
||||
});
|
||||
|
||||
test('throws if prefix is not passed', function() {
|
||||
assert.throws(function() {
|
||||
(new ErrorDialog({}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('uses correct Dialog options', function() {
|
||||
test('confirm handler is not set, errorDescription.executeHandler is true',
|
||||
function() {
|
||||
var errorDescription = {
|
||||
prefix: 'TEST',
|
||||
executeHandler: true
|
||||
};
|
||||
|
||||
(new ErrorDialog(errorDescription));
|
||||
|
||||
sinon.assert.calledWith(Dialog.call, sinon.match.any, {
|
||||
title: sinon.match.any,
|
||||
body: sinon.match.any,
|
||||
options: {
|
||||
cancel: {
|
||||
text: errorDescription.prefix + 'BtnOk'
|
||||
}
|
||||
},
|
||||
classes: ['error-dialog-' + errorDescription.prefix, 'error-dialog']
|
||||
});
|
||||
});
|
||||
|
||||
test('confirm handler is set, errorDescription.executeHandler is false',
|
||||
function() {
|
||||
var errorDescription = {
|
||||
prefix: 'TEST',
|
||||
executeHandler: false
|
||||
};
|
||||
var dialogOptions = { confirmHandler: () => {} };
|
||||
|
||||
(new ErrorDialog(errorDescription, dialogOptions));
|
||||
|
||||
sinon.assert.calledWith(Dialog.call, sinon.match.any, {
|
||||
title: sinon.match.any,
|
||||
body: sinon.match.any,
|
||||
options: {
|
||||
cancel: {
|
||||
text: errorDescription.prefix + 'BtnOk'
|
||||
}
|
||||
},
|
||||
classes: ['error-dialog-' + errorDescription.prefix, 'error-dialog']
|
||||
});
|
||||
});
|
||||
|
||||
test('confirm handler is set, errorDescription.executeHandler is true',
|
||||
function() {
|
||||
var errorDescription = {
|
||||
prefix: 'TEST',
|
||||
executeHandler: true
|
||||
};
|
||||
var dialogOptions = { confirmHandler: () => {} };
|
||||
|
||||
(new ErrorDialog(errorDescription, dialogOptions));
|
||||
|
||||
sinon.assert.calledWith(Dialog.call, sinon.match.any, {
|
||||
title: sinon.match.any,
|
||||
body: sinon.match.any,
|
||||
options: {
|
||||
cancel: {
|
||||
text: errorDescription.prefix + 'BtnOk'
|
||||
},
|
||||
confirm: {
|
||||
text: errorDescription.prefix + 'Confirm',
|
||||
method: dialogOptions.confirmHandler
|
||||
}
|
||||
},
|
||||
classes: ['error-dialog-' + errorDescription.prefix, 'error-dialog']
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('respects errorDescription.showRecipient', function() {
|
||||
test('errorDescription.showRecipient is true, recipients array is set',
|
||||
function() {
|
||||
var errorDescription = {
|
||||
prefix: 'TEST',
|
||||
showRecipient: true
|
||||
};
|
||||
|
||||
var dialogOptions = {
|
||||
recipients: ['111', '222', '333']
|
||||
};
|
||||
|
||||
(new ErrorDialog(errorDescription, dialogOptions));
|
||||
|
||||
var fragmentContainsBody = sinon.match((fragment) => {
|
||||
var body = fragment.querySelector('span');
|
||||
var expectedL10nId = errorDescription.prefix + 'Body';
|
||||
var hasL10nId = (body.dataset.l10nId === expectedL10nId);
|
||||
var expectedArgs = JSON.stringify(
|
||||
{ n: dialogOptions.recipients.length }
|
||||
);
|
||||
var hasL10nArgs = (body.dataset.l10nArgs === expectedArgs);
|
||||
|
||||
return hasL10nId && hasL10nArgs;
|
||||
}, 'fragment contains correct body');
|
||||
|
||||
var fragmentContainsRecipients = sinon.match((fragment) => {
|
||||
var recipients = fragment.querySelector('ul');
|
||||
return dialogOptions.recipients.every((recipient, i) => {
|
||||
var li = recipients.children[i];
|
||||
return li.textContent === recipient;
|
||||
});
|
||||
}, 'fragment contains all recipients');
|
||||
|
||||
var fragmentContainsBodyAndRecipients =
|
||||
sinon.match.instanceOf(DocumentFragment)
|
||||
.and(fragmentContainsBody)
|
||||
.and(fragmentContainsRecipients);
|
||||
|
||||
var expectedClasses = [
|
||||
'error-dialog-TEST', 'error-dialog', 'error-dialog-show-recipient'
|
||||
];
|
||||
|
||||
sinon.assert.calledWithMatch(Dialog.call, sinon.match.any, {
|
||||
body: {
|
||||
raw: fragmentContainsBodyAndRecipients
|
||||
},
|
||||
classes: expectedClasses
|
||||
});
|
||||
});
|
||||
|
||||
test('errorDescription.showRecipient is true, recipients array is not set',
|
||||
function() {
|
||||
var errorDescription = {
|
||||
prefix: 'TEST',
|
||||
showRecipient: true
|
||||
};
|
||||
|
||||
assert.throws(function() {
|
||||
(new ErrorDialog(errorDescription));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('respects errorDescription.showDsdsStatus', function() {
|
||||
test('errorDescription.showDsdsStatus is true, SIM 1 is active',
|
||||
function() {
|
||||
var errorDescription = {
|
||||
prefix: 'TEST',
|
||||
showDsdsStatus: true
|
||||
};
|
||||
|
||||
Settings.mmsServiceId = 0;
|
||||
|
||||
(new ErrorDialog(errorDescription));
|
||||
sinon.assert.calledWith(Dialog.call, sinon.match.any, {
|
||||
title: sinon.match.any,
|
||||
body: {
|
||||
id: errorDescription.prefix + 'Body',
|
||||
args: {
|
||||
activeSimId: '1',
|
||||
nonActiveSimId: '2'
|
||||
}
|
||||
},
|
||||
options: sinon.match.any,
|
||||
classes: ['error-dialog-' + errorDescription.prefix, 'error-dialog']
|
||||
});
|
||||
});
|
||||
|
||||
test('errorDescription.showDsdsStatus is true, SIM 2 is active',
|
||||
function() {
|
||||
var errorDescription = {
|
||||
prefix: 'TEST',
|
||||
showDsdsStatus: true
|
||||
};
|
||||
|
||||
Settings.mmsServiceId = 1;
|
||||
|
||||
(new ErrorDialog(errorDescription));
|
||||
sinon.assert.calledWith(Dialog.call, sinon.match.any, {
|
||||
title: sinon.match.any,
|
||||
body: {
|
||||
id: errorDescription.prefix + 'Body',
|
||||
args: {
|
||||
activeSimId: '2',
|
||||
nonActiveSimId: '1'
|
||||
}
|
||||
},
|
||||
options: sinon.match.any,
|
||||
classes: ['error-dialog-' + errorDescription.prefix, 'error-dialog']
|
||||
});
|
||||
});
|
||||
|
||||
test('errorDescription.showDsdsStatus is false, any SIM is active',
|
||||
function() {
|
||||
var errorDescription = {
|
||||
prefix: 'TEST',
|
||||
showDsdsStatus: false
|
||||
};
|
||||
|
||||
Settings.mmsServiceId = 0;
|
||||
|
||||
(new ErrorDialog(errorDescription));
|
||||
sinon.assert.calledWith(Dialog.call, sinon.match.any, {
|
||||
title: sinon.match.any,
|
||||
body: errorDescription.prefix + 'Body',
|
||||
options: sinon.match.any,
|
||||
classes: ['error-dialog-' + errorDescription.prefix, 'error-dialog']
|
||||
});
|
||||
});
|
||||
|
||||
test('errorDescription.showDsdsStatus is true, mmsServiceId is not valid',
|
||||
function() {
|
||||
var errorDescription = {
|
||||
prefix: 'TEST',
|
||||
showDsdsStatus: true
|
||||
};
|
||||
|
||||
// No mmsServiceId available
|
||||
Settings.mmsServiceId = null;
|
||||
|
||||
(new ErrorDialog(errorDescription));
|
||||
sinon.assert.calledWith(Dialog.call, sinon.match.any, {
|
||||
title: sinon.match.any,
|
||||
body: {
|
||||
id: errorDescription.prefix + 'Body',
|
||||
args: null
|
||||
},
|
||||
options: sinon.match.any,
|
||||
classes: ['error-dialog-' + errorDescription.prefix, 'error-dialog']
|
||||
});
|
||||
Dialog.call.reset();
|
||||
|
||||
// Unknown mmsServiceId
|
||||
Settings.mmsServiceId = 2;
|
||||
|
||||
(new ErrorDialog(errorDescription));
|
||||
sinon.assert.calledWith(Dialog.call, sinon.match.any, {
|
||||
title: sinon.match.any,
|
||||
body: {
|
||||
id: errorDescription.prefix + 'Body',
|
||||
args: null
|
||||
},
|
||||
options: sinon.match.any,
|
||||
classes: ['error-dialog-' + errorDescription.prefix, 'error-dialog']
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
34
gaia/apps/sms/views/shared/test/unit/errors_test.js
Executable file
34
gaia/apps/sms/views/shared/test/unit/errors_test.js
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
/*global Errors */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/js/errors.js');
|
||||
|
||||
suite('Errors', function() {
|
||||
test('Gets default value for unknown error code', function() {
|
||||
var defaultError = Errors.get('UnknownError');
|
||||
|
||||
assert.isNotNull(defaultError);
|
||||
assert.isString(defaultError.prefix);
|
||||
assert.deepEqual(defaultError, Errors.get('I do not know this error!'));
|
||||
});
|
||||
|
||||
test('Gets correct error description for known error code', function() {
|
||||
var defaultError = Errors.get('UnknownError');
|
||||
|
||||
var knownErrorCodes = [
|
||||
'NoSignalError', 'NotFoundError', 'FdnCheckError', 'InvalidAddressError',
|
||||
'NoSimCardError', 'RadioDisabledError', 'NonActiveSimCardError',
|
||||
'SimNotMatchedError', 'NonActiveSimCardToSendError',
|
||||
'RadioDisabledToDownloadError'
|
||||
];
|
||||
|
||||
knownErrorCodes.forEach((code) => {
|
||||
var error = Errors.get(code);
|
||||
|
||||
assert.isNotNull(error);
|
||||
assert.isString(error.prefix);
|
||||
assert.notEqual(error, defaultError);
|
||||
});
|
||||
});
|
||||
});
|
||||
100
gaia/apps/sms/views/shared/test/unit/fixture_phones.js
Executable file
100
gaia/apps/sms/views/shared/test/unit/fixture_phones.js
Executable file
|
|
@ -0,0 +1,100 @@
|
|||
/*exported FixturePhones */
|
||||
|
||||
'use strict';
|
||||
/**
|
||||
* Derived from
|
||||
* /dom/phonenumberutils/tests/test_phonenumber.xul
|
||||
*
|
||||
* isTestable: true|false
|
||||
*
|
||||
* Fixtures marked isTestable: false are effectively
|
||||
* placeholders that stand-in to show the correct
|
||||
* behaviour in cases where
|
||||
* navigator.mozPhoneNumberService.normalize is
|
||||
* available (sufficient permissions required)
|
||||
*
|
||||
*
|
||||
* title: string
|
||||
*
|
||||
* Description used for the suite title.
|
||||
*
|
||||
* values: array
|
||||
*
|
||||
* Array of values that are tested by comparing
|
||||
* each value to every other value.
|
||||
*
|
||||
*/
|
||||
var FixturePhones = [
|
||||
{
|
||||
// This is only tested where there are sufficient permissions
|
||||
// to use navigator.mozPhoneNumberService.normalize
|
||||
isTestable: !!(navigator.mozPhoneNumberService &&
|
||||
navigator.mozPhoneNumberService.normalize),
|
||||
title: 'Non-Digit in Contact',
|
||||
values: [
|
||||
'1-800-BUY-A-CAR', '1 800 BUY A CAR', '1 (800) BUY A CAR',
|
||||
'+18002892227', '18002892227', '8002892227'
|
||||
]
|
||||
},
|
||||
{
|
||||
isTestable: true,
|
||||
title: 'US',
|
||||
values: [
|
||||
'9995551234', '+19995551234', '(999) 555-1234',
|
||||
'1 (999) 555-1234', '+1 (999) 555-1234', '+1 999-555-1234'
|
||||
]
|
||||
},
|
||||
{
|
||||
isTestable: true,
|
||||
title: 'DE',
|
||||
values: [
|
||||
'01149451491934', '49451491934', '451491934',
|
||||
'0451 491934', '+49 451 491934', '+49451491934'
|
||||
]
|
||||
},
|
||||
{
|
||||
isTestable: true,
|
||||
title: 'IT',
|
||||
values: [
|
||||
'0577-555-555', '0577555555', '05 7755 5555', '+39 05 7755 5555'
|
||||
]
|
||||
},
|
||||
{
|
||||
isTestable: true,
|
||||
title: 'ES',
|
||||
values: [
|
||||
'612123123', '612 12 31 23', '+34 612 12 31 23'
|
||||
]
|
||||
},
|
||||
{
|
||||
isTestable: true,
|
||||
title: 'BR',
|
||||
values: [
|
||||
'01187654321', '0411187654321', '551187654321',
|
||||
'90411187654321', '+551187654321'
|
||||
]
|
||||
},
|
||||
{
|
||||
isTestable: true,
|
||||
title: 'CL',
|
||||
values: [
|
||||
'0997654321', '997654321', '(99) 765 4321', '+56 99 765 4321'
|
||||
]
|
||||
},
|
||||
{
|
||||
isTestable: true,
|
||||
title: 'CO',
|
||||
values: [
|
||||
'5712234567', '12234567', '(1) 2234567', '+57 1 2234567'
|
||||
]
|
||||
},
|
||||
{
|
||||
isTestable: true,
|
||||
title: 'FR',
|
||||
values: [
|
||||
'0123456789', '+33123456789', '0033123456789',
|
||||
'01.23.45.67.89', '01 23 45 67 89', '01-23-45-67-89',
|
||||
'+33 1 23 45 67 89', '+33 (1) 23456789'
|
||||
]
|
||||
}
|
||||
];
|
||||
121
gaia/apps/sms/views/shared/test/unit/inter_instance_event_dispatcher_test.js
Executable file
121
gaia/apps/sms/views/shared/test/unit/inter_instance_event_dispatcher_test.js
Executable file
|
|
@ -0,0 +1,121 @@
|
|||
/*global InterInstanceEventDispatcher,
|
||||
MockBroadcastChannel,
|
||||
BroadcastChannel
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/event_dispatcher.js');
|
||||
|
||||
require('/views/shared/js/inter_instance_event_dispatcher.js');
|
||||
require('/views/shared/test/unit/mock_broadcast_channel.js');
|
||||
|
||||
suite('InterInstanceEventDispatcher >', function() {
|
||||
var realBroadcastChannel;
|
||||
|
||||
suiteSetup(function() {
|
||||
realBroadcastChannel = Object.getOwnPropertyDescriptor(
|
||||
window, 'BroadcastChannel'
|
||||
);
|
||||
window.BroadcastChannel = MockBroadcastChannel;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
Object.defineProperty(window, 'BroadcastChannel', realBroadcastChannel);
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sinon.spy(window, 'BroadcastChannel');
|
||||
this.sinon.stub(BroadcastChannel.prototype, 'addEventListener');
|
||||
this.sinon.stub(BroadcastChannel.prototype, 'removeEventListener');
|
||||
this.sinon.stub(BroadcastChannel.prototype, 'close');
|
||||
this.sinon.stub(BroadcastChannel.prototype, 'postMessage');
|
||||
this.sinon.spy(window, 'addEventListener');
|
||||
|
||||
InterInstanceEventDispatcher.connect();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
InterInstanceEventDispatcher.disconnect();
|
||||
});
|
||||
|
||||
test('correctly connects to BroadcastChannel', function() {
|
||||
sinon.assert.calledWith(BroadcastChannel, 'messages-channel');
|
||||
sinon.assert.calledWith(
|
||||
BroadcastChannel.prototype.addEventListener,
|
||||
'message'
|
||||
);
|
||||
|
||||
InterInstanceEventDispatcher.connect();
|
||||
InterInstanceEventDispatcher.connect();
|
||||
sinon.assert.calledOnce(BroadcastChannel);
|
||||
|
||||
InterInstanceEventDispatcher.disconnect();
|
||||
InterInstanceEventDispatcher.connect();
|
||||
sinon.assert.calledTwice(BroadcastChannel);
|
||||
});
|
||||
|
||||
test('correctly disconnects from BroadcastChannel', function() {
|
||||
var addEventListenerArgs =
|
||||
BroadcastChannel.prototype.addEventListener.lastCall.args;
|
||||
window.addEventListener.withArgs('unload').yield();
|
||||
|
||||
sinon.assert.called(BroadcastChannel.prototype.close);
|
||||
sinon.assert.calledWith(
|
||||
BroadcastChannel.prototype.removeEventListener,
|
||||
addEventListenerArgs[0],
|
||||
// Reference to "onmessage" listener function
|
||||
addEventListenerArgs[1]
|
||||
);
|
||||
});
|
||||
|
||||
test('correctly handles incoming BroadcastChannel messages', function() {
|
||||
BroadcastChannel.prototype.postMessage.reset();
|
||||
|
||||
var onDraftsChangedStub = sinon.stub();
|
||||
InterInstanceEventDispatcher.on('draft-saved', onDraftsChangedStub);
|
||||
|
||||
BroadcastChannel.prototype.addEventListener.withArgs('message').yield({
|
||||
data: {
|
||||
name: 'draft-saved',
|
||||
parameters: { key: 'value' }
|
||||
}
|
||||
});
|
||||
|
||||
sinon.assert.calledWith(onDraftsChangedStub, { key: 'value' });
|
||||
|
||||
BroadcastChannel.prototype.addEventListener.withArgs('message').yield({
|
||||
data: {
|
||||
name: 'draft-saved',
|
||||
parameters: { key: 'value#2' }
|
||||
}
|
||||
});
|
||||
|
||||
sinon.assert.calledTwice(onDraftsChangedStub);
|
||||
sinon.assert.calledWith(onDraftsChangedStub, { key: 'value#2' });
|
||||
|
||||
sinon.assert.notCalled(BroadcastChannel.prototype.postMessage);
|
||||
});
|
||||
|
||||
test('correctly handles outgoing BroadcastChannel messages', function() {
|
||||
var onDraftsChangedStub = sinon.stub();
|
||||
InterInstanceEventDispatcher.on('draft-saved', onDraftsChangedStub);
|
||||
|
||||
assert.throws(() => {
|
||||
InterInstanceEventDispatcher.emit('something-changed');
|
||||
}, 'Event "something-changed" is not allowed!');
|
||||
|
||||
InterInstanceEventDispatcher.disconnect();
|
||||
assert.throws(() => {
|
||||
InterInstanceEventDispatcher.emit('draft-saved', { key: 'value' });
|
||||
}, 'Channel is not created!');
|
||||
InterInstanceEventDispatcher.connect();
|
||||
|
||||
InterInstanceEventDispatcher.emit('draft-saved', { key: 'value' });
|
||||
sinon.assert.calledWith(BroadcastChannel.prototype.postMessage, {
|
||||
name: 'draft-saved',
|
||||
parameters: { key: 'value' }
|
||||
});
|
||||
sinon.assert.notCalled(onDraftsChangedStub, 'Does not broadcast to itself');
|
||||
});
|
||||
});
|
||||
217
gaia/apps/sms/views/shared/test/unit/localization_helper_test.js
Executable file
217
gaia/apps/sms/views/shared/test/unit/localization_helper_test.js
Executable file
|
|
@ -0,0 +1,217 @@
|
|||
/*global LocalizationHelper,
|
||||
MockL10n
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/js/localization_helper.js');
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l20n.js');
|
||||
|
||||
suite('LocalizationHelper >', function() {
|
||||
var navigatorMozL10n;
|
||||
|
||||
function onDOMRetranslated() {
|
||||
document.addEventListener.withArgs('DOMRetranslated').yield();
|
||||
return false;
|
||||
}
|
||||
|
||||
function onTimeFormatChange() {
|
||||
window.addEventListener.withArgs('timeformatchange').yield();
|
||||
return true;
|
||||
}
|
||||
|
||||
setup(function(done) {
|
||||
navigatorMozL10n = document.l10n;
|
||||
document.l10n = MockL10n;
|
||||
|
||||
this.sinon.stub(document.l10n, 'translateFragment');
|
||||
this.sinon.stub(window, 'addEventListener');
|
||||
this.sinon.stub(document, 'addEventListener');
|
||||
|
||||
const getAttr = this.sinon.stub(document.documentElement, 'getAttribute');
|
||||
getAttr.withArgs('lang').returns('be-BY');
|
||||
getAttr.withArgs('dir').returns('rtl');
|
||||
|
||||
[
|
||||
'attachment-container',
|
||||
'attachment-container',
|
||||
'custom'
|
||||
].forEach((iframeClassName) => {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.className = iframeClassName;
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
|
||||
var customIframeDocument = document.querySelector(
|
||||
'iframe.custom'
|
||||
).contentDocument;
|
||||
customIframeDocument.documentElement.lang = 'en-US';
|
||||
customIframeDocument.documentElement.dir = 'ltr';
|
||||
|
||||
LocalizationHelper.init().then(
|
||||
() => undefined).then(done, done);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
document.l10n = navigatorMozL10n;
|
||||
});
|
||||
|
||||
test('localizes only attachment iframes', function() {
|
||||
sinon.assert.calledTwice(document.l10n.translateFragment);
|
||||
var attachmentContainers = document.querySelectorAll(
|
||||
'iframe.attachment-container'
|
||||
);
|
||||
Array.forEach(attachmentContainers, (iframe) => {
|
||||
var doc = iframe.contentDocument;
|
||||
assert.equal(doc.documentElement.lang, 'be-BY');
|
||||
assert.equal(doc.documentElement.dir, 'rtl');
|
||||
sinon.assert.calledWith(document.l10n.translateFragment, doc.body);
|
||||
});
|
||||
|
||||
// Custom iframe should stay untouched.
|
||||
var customIframeDocument = document.querySelector(
|
||||
'iframe.custom'
|
||||
).contentDocument;
|
||||
assert.equal(customIframeDocument.documentElement.lang, 'en-US');
|
||||
assert.equal(customIframeDocument.documentElement.dir, 'ltr');
|
||||
});
|
||||
|
||||
[onDOMRetranslated, onTimeFormatChange].forEach(function(forceUpdateMethod) {
|
||||
suite('localizes date & time ' + forceUpdateMethod.name +' >', function() {
|
||||
var node;
|
||||
|
||||
setup(function() {
|
||||
node = document.createElement('div');
|
||||
node.classList.add('l10n-contains-date');
|
||||
document.body.appendChild(node);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
node.remove();
|
||||
});
|
||||
|
||||
test('ignores if date or format are unavailable', function() {
|
||||
node.dataset.l10nDate = '';
|
||||
node.dataset.l10nDateFormat = 'format';
|
||||
node.textContent = 'not-changed-content';
|
||||
|
||||
forceUpdateMethod();
|
||||
|
||||
assert.equal(node.textContent, 'not-changed-content');
|
||||
assert.isFalse(node.hasAttribute('data-l10n-id'));
|
||||
assert.isFalse(node.hasAttribute('data-l10n-args'));
|
||||
|
||||
node.dataset.l10nDate = Date.now();
|
||||
node.dataset.l10nDateFormat = '';
|
||||
node.textContent = 'not-changed-content';
|
||||
|
||||
forceUpdateMethod();
|
||||
|
||||
assert.equal(node.textContent, 'not-changed-content');
|
||||
assert.isFalse(node.hasAttribute('data-l10n-id'));
|
||||
assert.isFalse(node.hasAttribute('data-l10n-args'));
|
||||
});
|
||||
|
||||
test('correctly formats node content', function() {
|
||||
var timestamp = Date.now();
|
||||
var options = {
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
year: 'numeric',
|
||||
};
|
||||
|
||||
function assertChange(options, doNotFormat) {
|
||||
options.hour12 = navigator.mozHour12;
|
||||
var formatter =
|
||||
new Intl.DateTimeFormat(navigator.languages, options);
|
||||
// In case of time format change date-only elements aren't updated
|
||||
if (doNotFormat) {
|
||||
assert.equal(
|
||||
node.textContent, '', 'Date-only elements should not be updated'
|
||||
);
|
||||
} else {
|
||||
assert.equal(
|
||||
node.textContent,
|
||||
formatter.format(new Date(timestamp))
|
||||
);
|
||||
}
|
||||
|
||||
assert.isFalse(node.hasAttribute('data-l10n-id'));
|
||||
assert.isFalse(node.hasAttribute('data-l10n-args'));
|
||||
}
|
||||
|
||||
node.dataset.l10nDate = timestamp;
|
||||
node.dataset.l10nDateFormat = JSON.stringify(options);
|
||||
// In case of time format change date-only elements shouldn't be updated
|
||||
assertChange(options, forceUpdateMethod());
|
||||
|
||||
options.hour = 'numeric';
|
||||
options.minute = 'numeric';
|
||||
node.dataset.l10nDateFormat = JSON.stringify(options);
|
||||
|
||||
navigator.mozHour12 = true;
|
||||
forceUpdateMethod();
|
||||
assertChange(options);
|
||||
|
||||
navigator.mozHour12 = false;
|
||||
forceUpdateMethod();
|
||||
|
||||
assertChange(options);
|
||||
});
|
||||
|
||||
test('correctly formats node l10n attributes', function() {
|
||||
var timestamp = Date.now();
|
||||
var options = {
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
year: 'numeric',
|
||||
};
|
||||
|
||||
function assertChange(options, doNotFormat) {
|
||||
options.hour12 = navigator.mozHour12;
|
||||
var formatter =
|
||||
new Intl.DateTimeFormat(navigator.languages, options);
|
||||
|
||||
var l10nAttributes = document.l10n.getAttributes(node);
|
||||
// In case of time format change date-only elements aren't updated
|
||||
if (doNotFormat) {
|
||||
assert.deepEqual(l10nAttributes.args, {
|
||||
data: 'custom'
|
||||
}, 'Date-only elements should not be updated');
|
||||
} else {
|
||||
assert.deepEqual(l10nAttributes.args, {
|
||||
data: 'custom',
|
||||
date: formatter.format(new Date(timestamp))
|
||||
});
|
||||
}
|
||||
|
||||
assert.equal(l10nAttributes.id, 'custom');
|
||||
assert.equal(node.textContent, 'not-changed-content');
|
||||
}
|
||||
|
||||
document.l10n.setAttributes(node, 'custom', {
|
||||
data: 'custom'
|
||||
});
|
||||
node.dataset.l10nDate = timestamp;
|
||||
node.dataset.l10nDateFormat = JSON.stringify(options);
|
||||
node.textContent = 'not-changed-content';
|
||||
// In case of time format change date-only elements shouldn't be updated
|
||||
assertChange(options, forceUpdateMethod());
|
||||
|
||||
options.hour = 'numeric';
|
||||
options.minute = 'numeric';
|
||||
node.dataset.l10nDateFormat = JSON.stringify(options);
|
||||
|
||||
navigator.mozHour12 = true;
|
||||
forceUpdateMethod();
|
||||
|
||||
assertChange(options);
|
||||
|
||||
navigator.mozHour12 = false;
|
||||
forceUpdateMethod();
|
||||
|
||||
assertChange(options);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
BIN
gaia/apps/sms/views/shared/test/unit/media/audio.oga
Executable file
BIN
gaia/apps/sms/views/shared/test/unit/media/audio.oga
Executable file
Binary file not shown.
6
gaia/apps/sms/views/shared/test/unit/media/contacts.vcf
Executable file
6
gaia/apps/sms/views/shared/test/unit/media/contacts.vcf
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
FN:dennis
|
||||
N:;dennis;;;
|
||||
TEL;TYPE=CELL:0975557726
|
||||
END:VCARD
|
||||
BIN
gaia/apps/sms/views/shared/test/unit/media/grid.png
Executable file
BIN
gaia/apps/sms/views/shared/test/unit/media/grid.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 363 B |
BIN
gaia/apps/sms/views/shared/test/unit/media/grid.wbmp
Executable file
BIN
gaia/apps/sms/views/shared/test/unit/media/grid.wbmp
Executable file
Binary file not shown.
BIN
gaia/apps/sms/views/shared/test/unit/media/video.ogv
Executable file
BIN
gaia/apps/sms/views/shared/test/unit/media/video.ogv
Executable file
Binary file not shown.
88
gaia/apps/sms/views/shared/test/unit/messages_mockup.js
Executable file
88
gaia/apps/sms/views/shared/test/unit/messages_mockup.js
Executable file
|
|
@ -0,0 +1,88 @@
|
|||
/*global getMockupedDate */
|
||||
/*exported MockThreadMessages */
|
||||
|
||||
'use strict';
|
||||
|
||||
function MockThreadMessages() {
|
||||
|
||||
var messagesMockup = [
|
||||
{
|
||||
sender: null,
|
||||
receiver: '197746797',
|
||||
body: 'Error message:)',
|
||||
delivery: 'sending',
|
||||
deliveryInfo: [{
|
||||
receiver: '197746797',
|
||||
deliveryStatus: 'pending',
|
||||
deliveryTimestamp: null,
|
||||
readStatus: 'not-applicable',
|
||||
readTimestamp: null
|
||||
}],
|
||||
error: true,
|
||||
id: 47,
|
||||
threadId: 1,
|
||||
timestamp: +getMockupedDate(0)
|
||||
},
|
||||
{
|
||||
sender: null,
|
||||
receiver: '197746797',
|
||||
body: 'Nothing :)',
|
||||
delivery: 'sent',
|
||||
deliveryInfo: [{
|
||||
receiver: '197746797',
|
||||
deliveryStatus: 'success',
|
||||
deliveryTimestamp: +getMockupedDate(0),
|
||||
readStatus: 'not-applicable',
|
||||
readTimestamp: null
|
||||
}],
|
||||
id: 46,
|
||||
threadId: 1,
|
||||
timestamp: +getMockupedDate(0)
|
||||
},
|
||||
{
|
||||
sender: '197746797',
|
||||
body: 'Recibido!',
|
||||
delivery: 'received',
|
||||
id: 40,
|
||||
threadId: 1,
|
||||
timestamp: +getMockupedDate(2)
|
||||
},
|
||||
{
|
||||
sender: null,
|
||||
receiver: '197746797',
|
||||
body: 'Nothing :)',
|
||||
delivery: 'error',
|
||||
deliveryInfo: [{
|
||||
receiver: '197746797',
|
||||
deliveryStatus: 'errpr',
|
||||
deliveryTimestamp: null,
|
||||
readStatus: 'not-applicable',
|
||||
readTimestamp: null
|
||||
}],
|
||||
id: 460,
|
||||
threadId: 1,
|
||||
timestamp: +getMockupedDate(6)
|
||||
},
|
||||
{
|
||||
sender: null,
|
||||
receiver: '197746797',
|
||||
body: 'Nothing at all :)',
|
||||
delivery: 'error',
|
||||
deliveryInfo: [{
|
||||
receiver: '197746797',
|
||||
deliveryStatus: 'error',
|
||||
deliveryTimestamp: null,
|
||||
readStatus: 'not-applicable',
|
||||
readTimestamp: null
|
||||
}],
|
||||
id: 461,
|
||||
threadId: 1,
|
||||
timestamp: +getMockupedDate(6)
|
||||
}];
|
||||
|
||||
messagesMockup.sort(function(a, b) {
|
||||
return b.timestamp - a.timestamp;
|
||||
});
|
||||
|
||||
return messagesMockup;
|
||||
}
|
||||
6
gaia/apps/sms/views/shared/test/unit/mock_activity_handler.js
Executable file
6
gaia/apps/sms/views/shared/test/unit/mock_activity_handler.js
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
/*exported MockActivityHandler */
|
||||
'use strict';
|
||||
|
||||
var MockActivityHandler = {
|
||||
init() {}
|
||||
};
|
||||
25
gaia/apps/sms/views/shared/test/unit/mock_activity_picker.js
Executable file
25
gaia/apps/sms/views/shared/test/unit/mock_activity_picker.js
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
var MockActivityPicker = {};
|
||||
|
||||
|
||||
[
|
||||
'email', 'dial', 'url',
|
||||
'createNewContact', 'addToExistingContact'
|
||||
].forEach(function(fn) {
|
||||
MockActivityPicker[fn] = function() {
|
||||
MockActivityPicker[fn].called = true;
|
||||
MockActivityPicker[fn].calledWith = [].slice.call(arguments);
|
||||
};
|
||||
|
||||
MockActivityPicker[fn].mSetup = function() {
|
||||
MockActivityPicker[fn].called = false;
|
||||
MockActivityPicker[fn].calledWith = null;
|
||||
};
|
||||
|
||||
MockActivityPicker[fn].mTeardown = function() {
|
||||
delete MockActivityPicker[fn].called;
|
||||
delete MockActivityPicker[fn].calledWith;
|
||||
};
|
||||
});
|
||||
11
gaia/apps/sms/views/shared/test/unit/mock_app.js
Executable file
11
gaia/apps/sms/views/shared/test/unit/mock_app.js
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
/* exported MockApp */
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
exports.MockApp = {
|
||||
instanceId: 'app-instance-id',
|
||||
whenReady: () => Promise.resolve(),
|
||||
setReady: () => {}
|
||||
};
|
||||
})(window);
|
||||
36
gaia/apps/sms/views/shared/test/unit/mock_async_storage.js
Executable file
36
gaia/apps/sms/views/shared/test/unit/mock_async_storage.js
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
/*exported MockasyncStorage */
|
||||
|
||||
'use strict';
|
||||
|
||||
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() {
|
||||
this.keys = {};
|
||||
},
|
||||
mSetup: function() {
|
||||
this.clear();
|
||||
},
|
||||
mTeardown: function() {
|
||||
this.clear();
|
||||
}
|
||||
};
|
||||
27
gaia/apps/sms/views/shared/test/unit/mock_attachment.js
Executable file
27
gaia/apps/sms/views/shared/test/unit/mock_attachment.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
/*global Utils */
|
||||
/*exported MockAttachment */
|
||||
|
||||
'use strict';
|
||||
|
||||
function MockAttachment(blob, options) {
|
||||
this.blob = blob;
|
||||
options = options || {};
|
||||
this.name = options.name;
|
||||
this.isDraft = !!options.isDraft;
|
||||
this.mNextRender = document.createElement('iframe');
|
||||
this.mNextRender.className = 'attachment-container';
|
||||
}
|
||||
|
||||
MockAttachment.prototype = {
|
||||
get size() {
|
||||
return this.blob && this.blob.size;
|
||||
},
|
||||
get type() {
|
||||
return Utils.typeFromMimeType(this.blob.type);
|
||||
},
|
||||
render: function() {
|
||||
return this.mNextRender;
|
||||
},
|
||||
view: function() {},
|
||||
updateFileSize: function() {}
|
||||
};
|
||||
15
gaia/apps/sms/views/shared/test/unit/mock_attachment_renderer.js
Executable file
15
gaia/apps/sms/views/shared/test/unit/mock_attachment_renderer.js
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
function MockAttachmentRenderer() {}
|
||||
|
||||
MockAttachmentRenderer.prototype = {
|
||||
render: function() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
getAttachmentContainer: function() {},
|
||||
updateFileSize: function() {}
|
||||
};
|
||||
|
||||
MockAttachmentRenderer.for = function() {
|
||||
return new MockAttachmentRenderer();
|
||||
};
|
||||
12
gaia/apps/sms/views/shared/test/unit/mock_broadcast_channel.js
Executable file
12
gaia/apps/sms/views/shared/test/unit/mock_broadcast_channel.js
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
/*exported MockBroadcastChannel */
|
||||
|
||||
'use strict';
|
||||
|
||||
function MockBroadcastChannel(name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
MockBroadcastChannel.prototype.addEventListener = () => {};
|
||||
MockBroadcastChannel.prototype.removeEventListener = () => {};
|
||||
MockBroadcastChannel.prototype.postMessage = () => {};
|
||||
MockBroadcastChannel.prototype.close = () => {};
|
||||
35
gaia/apps/sms/views/shared/test/unit/mock_compose.js
Executable file
35
gaia/apps/sms/views/shared/test/unit/mock_compose.js
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
/*exported MockCompose */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockCompose = {
|
||||
isSubjectVisible: false,
|
||||
|
||||
init: function() {},
|
||||
on: function(type, handler) {},
|
||||
off: function(type, handler) {},
|
||||
offAll: function() {},
|
||||
getContent: function() {},
|
||||
getText: function() {},
|
||||
isEmpty: () => true,
|
||||
isSubjectMaxLength: () => false,
|
||||
setupLock() {},
|
||||
refresh() {},
|
||||
scrollToTarget: function(target) {},
|
||||
scrollMessageContent: function() {},
|
||||
prepend: function(item) {},
|
||||
append: () => {},
|
||||
clear: () => {},
|
||||
focus: function() {},
|
||||
|
||||
getSubject: function() {},
|
||||
setSubject: function() {},
|
||||
showSubject: function() {},
|
||||
hideSubject: function() {},
|
||||
fromDraft: function() {},
|
||||
|
||||
mSetup: function() {
|
||||
this.size = null;
|
||||
this.type = 'sms';
|
||||
}
|
||||
};
|
||||
70
gaia/apps/sms/views/shared/test/unit/mock_contact.js
Executable file
70
gaia/apps/sms/views/shared/test/unit/mock_contact.js
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
function MockContact(name) {
|
||||
name = typeof name !== 'undefined' ? name : {
|
||||
familyName: ['O\'Hare'],
|
||||
givenName: ['Pepito']
|
||||
};
|
||||
if (!(this instanceof MockContact)) {
|
||||
return new MockContact(name);
|
||||
}
|
||||
this.id = '1';
|
||||
|
||||
this.familyName = name.familyName;
|
||||
this.givenName = name.givenName;
|
||||
this.name = [
|
||||
[this.givenName, this.familyName].join(' ')
|
||||
];
|
||||
|
||||
this.tel = [
|
||||
{
|
||||
'value': '+346578888888',
|
||||
'type': ['Mobile'],
|
||||
'carrier': 'TEF'
|
||||
},
|
||||
{
|
||||
'value': '+12125559999',
|
||||
'type': 'Batphone', // Make sure we cover both strings and arrays
|
||||
'carrier': 'XXX'
|
||||
}
|
||||
];
|
||||
|
||||
this.email = [
|
||||
{
|
||||
'value': 'a@b.com'
|
||||
}
|
||||
];
|
||||
|
||||
this.category = [
|
||||
'favorite'
|
||||
];
|
||||
|
||||
this.org = [
|
||||
''
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
MockContact.list([names])
|
||||
|
||||
Create contacts lists
|
||||
|
||||
MockContact.list();
|
||||
|
||||
MockContact.list([
|
||||
{ givenName: ['Jane'], familyName: ['Doozer'] },
|
||||
{ givenName: ['doug'], familyName: ['dooley'] }
|
||||
]);
|
||||
*/
|
||||
|
||||
MockContact.list = function(names) {
|
||||
if (names && names.length) {
|
||||
return names.map(function(name) {
|
||||
return new MockContact(name);
|
||||
});
|
||||
} else {
|
||||
return [new MockContact()];
|
||||
}
|
||||
};
|
||||
14
gaia/apps/sms/views/shared/test/unit/mock_contact_renderer.js
Executable file
14
gaia/apps/sms/views/shared/test/unit/mock_contact_renderer.js
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
/*exported MockContactRenderer */
|
||||
|
||||
'use strict';
|
||||
|
||||
function MockContactRenderer(opts) {
|
||||
}
|
||||
|
||||
MockContactRenderer.prototype = {
|
||||
render: function() {}
|
||||
};
|
||||
|
||||
MockContactRenderer.flavor = function() {
|
||||
return new MockContactRenderer();
|
||||
};
|
||||
23
gaia/apps/sms/views/shared/test/unit/mock_contacts.js
Executable file
23
gaia/apps/sms/views/shared/test/unit/mock_contacts.js
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
/*global MockContact */
|
||||
/*exported MockContacts */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/test/unit/mock_contact.js');
|
||||
|
||||
(function(exports) {
|
||||
function returnMock() {
|
||||
var result = MockContact.list();
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
exports.MockContacts = {
|
||||
findBy: returnMock,
|
||||
findByPhoneNumber: returnMock,
|
||||
findByAddress: returnMock,
|
||||
findExactByEmail: returnMock,
|
||||
findByString: returnMock,
|
||||
findExact: returnMock,
|
||||
addUnknown: () => {},
|
||||
};
|
||||
})(window);
|
||||
102
gaia/apps/sms/views/shared/test/unit/mock_conversation.js
Executable file
102
gaia/apps/sms/views/shared/test/unit/mock_conversation.js
Executable file
|
|
@ -0,0 +1,102 @@
|
|||
/*global MockRecipients, Template */
|
||||
/*exported MockConversationView */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/test/unit/mock_recipients.js');
|
||||
|
||||
var MockConversationView = {
|
||||
CHUNK_SIZE: 10,
|
||||
CONVERTED_MESSAGE_DURATION: 3000,
|
||||
IMAGE_RESIZE_DURATION: 3000,
|
||||
recipients: {
|
||||
add: function() {},
|
||||
focus: function() {}
|
||||
},
|
||||
recipientsList: document.createElement('div'),
|
||||
|
||||
// For Information view testing. Need to be updated with Conversation View
|
||||
// layout.
|
||||
optionsButton: document.createElement('button'),
|
||||
callNumberButton: document.createElement('button'),
|
||||
attachButton: document.createElement('button'),
|
||||
subheader: document.createElement('div'),
|
||||
container: document.createElement('article'),
|
||||
composeForm: document.createElement('form'),
|
||||
headerText: document.createElement('h1'),
|
||||
|
||||
inEditMode: false,
|
||||
init: function() {},
|
||||
initRecipients: function() {
|
||||
this.recipients = new MockRecipients({
|
||||
outer: 'messages-to-field',
|
||||
inner: 'messages-recipients-list',
|
||||
template: new Template('messages-recipient-tmpl')
|
||||
});
|
||||
},
|
||||
on: function() {},
|
||||
once: () => {},
|
||||
initSentAudio: function() {},
|
||||
getIdIterator: function() {},
|
||||
onMessageContentChange: function() {},
|
||||
assimilateRecipients: function() {},
|
||||
resizeHandler: function() {},
|
||||
requestContact: function() {},
|
||||
updateComposerHeader: function() {},
|
||||
isScrolledManually: false,
|
||||
manageScroll: function() {},
|
||||
scrollViewToBottom: function() {},
|
||||
back: function() {},
|
||||
isKeyboardDisplayed: function() {},
|
||||
getMessageContainer: function() {},
|
||||
updateHeaderData: function() {},
|
||||
initializeRendering: function() {},
|
||||
stopRendering: function() {},
|
||||
showFirstChunk: function() {},
|
||||
createMmsContent: function() {},
|
||||
renderMessages: function() {},
|
||||
_createNotDownloadedHTML: function() {},
|
||||
buildMessageDOM: function() {},
|
||||
appendMessage: function() {},
|
||||
showChunkOfMessages: function() {},
|
||||
setHeaderAction: function() {},
|
||||
setHeaderContent: function() {},
|
||||
cleanForm: function() {},
|
||||
clear: function() {},
|
||||
toggleCheckedAll: function() {},
|
||||
startEdit: function() {},
|
||||
delete: function() {},
|
||||
cancelEdit: function() {},
|
||||
updateSelectionStatus: function() {},
|
||||
handleMessageClick: function() {},
|
||||
handleEvent: function() {},
|
||||
cleanFields: function() {},
|
||||
onSendClick: function() {},
|
||||
onMessageSending: function() {},
|
||||
onMessageSent: function() {},
|
||||
onMessageFailed: function() {},
|
||||
onDeliverySuccess: function() {},
|
||||
onReadSuccess: function() {},
|
||||
removeMessageDOM: function() {},
|
||||
retrieveMMS: function() {},
|
||||
resendMessage: function() {},
|
||||
toFieldKeypress: function() {},
|
||||
toFieldInput: function() {},
|
||||
searchContact: function() {},
|
||||
onHeaderActivation: function() {},
|
||||
promptContact: () => Promise.resolve(),
|
||||
prompt: () => Promise.resolve(),
|
||||
saveDraft: function() {},
|
||||
discardDraft: function() {},
|
||||
isShowMessageErrorCalledTimes: 0,
|
||||
showMessageError: function() {
|
||||
this.isShowMessageErrorCalledTimes += 1;
|
||||
},
|
||||
mSetup: function() {
|
||||
this.isShowMessageErrorCalledTimes = 0;
|
||||
},
|
||||
|
||||
mTeardown: function() {
|
||||
this.isShowMessageErrorCalledTimes = 0;
|
||||
}
|
||||
};
|
||||
57
gaia/apps/sms/views/shared/test/unit/mock_dialog.js
Executable file
57
gaia/apps/sms/views/shared/test/unit/mock_dialog.js
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
'use strict';
|
||||
|
||||
function MockDialog(params) {
|
||||
MockDialog.calls.push(params);
|
||||
MockDialog.instances.push(this);
|
||||
|
||||
Object.keys(params.options).forEach(function(option) {
|
||||
MockDialog.triggers[option] = function() {
|
||||
MockDialog.triggers[option].called = true;
|
||||
params.options[option].method();
|
||||
};
|
||||
|
||||
// Setup the initial |false| value, this prevents assert.isFalse from
|
||||
// failing on |undefined| trigger properties
|
||||
MockDialog.triggers[option].called = false;
|
||||
});
|
||||
}
|
||||
|
||||
MockDialog.triggers = {};
|
||||
|
||||
MockDialog.prototype = {
|
||||
show: function() {
|
||||
this.show.called = true;
|
||||
}
|
||||
};
|
||||
|
||||
MockDialog.mSetup = function() {
|
||||
MockDialog.calls = [];
|
||||
MockDialog.instances = [];
|
||||
};
|
||||
|
||||
MockDialog.mTeardown = function() {
|
||||
delete MockDialog.calls;
|
||||
delete MockDialog.instances;
|
||||
|
||||
Object.keys(MockDialog.triggers).forEach(function(trigger) {
|
||||
MockDialog.triggers[trigger].called = false;
|
||||
});
|
||||
};
|
||||
|
||||
function MockErrorDialog() {
|
||||
MockErrorDialog.calls.push(arguments);
|
||||
}
|
||||
|
||||
MockErrorDialog.prototype = {
|
||||
show: function() {
|
||||
this.show.called = true;
|
||||
}
|
||||
};
|
||||
|
||||
MockErrorDialog.mSetup = function() {
|
||||
MockErrorDialog.calls = [];
|
||||
};
|
||||
|
||||
MockErrorDialog.mTeardown = function() {
|
||||
delete MockErrorDialog.calls;
|
||||
};
|
||||
38
gaia/apps/sms/views/shared/test/unit/mock_inbox.js
Executable file
38
gaia/apps/sms/views/shared/test/unit/mock_inbox.js
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
/*exported MockInboxView */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockInboxView = {
|
||||
count: 0,
|
||||
inEditMode: false,
|
||||
container: document.createElement('div'),
|
||||
init: function() {},
|
||||
updateThread: function() {},
|
||||
getIdIterator: function() {},
|
||||
setContact: function() {},
|
||||
handleEvent: function() {},
|
||||
updateSelectionStatus: function() {},
|
||||
cleanForm: function() {},
|
||||
toggleCheckedAll: function() {},
|
||||
removeConversationDOM: function() {},
|
||||
delete: function() {},
|
||||
setEmpty: function() {},
|
||||
showOptions: function() {},
|
||||
startEdit: function() {},
|
||||
cancelEdit: function() {},
|
||||
renderThreads: () => {},
|
||||
createThread: function() {},
|
||||
insertThreadContainer: function() {},
|
||||
onMessageSending: function() {},
|
||||
onMessageReceived: function() {},
|
||||
onThreadsDeleted: function() {},
|
||||
appendThread: function() {},
|
||||
onDraftSaved: function() {},
|
||||
onDraftDeleted: () => {},
|
||||
showDraftSavedBanner: () => {},
|
||||
createThreadContainer: function() {},
|
||||
updateContactsInfo: function() {},
|
||||
mark: function() {},
|
||||
markReadUnread: () => {},
|
||||
once: () => {}
|
||||
};
|
||||
18
gaia/apps/sms/views/shared/test/unit/mock_information.js
Executable file
18
gaia/apps/sms/views/shared/test/unit/mock_information.js
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
/*exported MockInformation, MockGroupView, MockReportView */
|
||||
'use strict';
|
||||
|
||||
function MockInformation(type) {
|
||||
}
|
||||
|
||||
MockInformation.initDefaultViews = () => {};
|
||||
|
||||
MockInformation.prototype = {
|
||||
onDeliverySuccess: function() {},
|
||||
onReadSuccess: function() {},
|
||||
show: function() {},
|
||||
refresh: function() {},
|
||||
reset: function() {}
|
||||
};
|
||||
|
||||
var MockGroupView = new MockInformation();
|
||||
var MockReportView = new MockInformation();
|
||||
10
gaia/apps/sms/views/shared/test/unit/mock_inter_instance_event_dispatcher.js
Executable file
10
gaia/apps/sms/views/shared/test/unit/mock_inter_instance_event_dispatcher.js
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
/*exported MockInterInstanceEventDispatcher */
|
||||
|
||||
'use strict';
|
||||
|
||||
function MockInterInstanceEventDispatcher() {}
|
||||
|
||||
MockInterInstanceEventDispatcher.on = () => {};
|
||||
MockInterInstanceEventDispatcher.emit = () => {};
|
||||
MockInterInstanceEventDispatcher.connect = () => {};
|
||||
MockInterInstanceEventDispatcher.disconnect = () => {};
|
||||
8
gaia/apps/sms/views/shared/test/unit/mock_link_action_handler.js
Executable file
8
gaia/apps/sms/views/shared/test/unit/mock_link_action_handler.js
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
/*exported MockLinkActionHandler */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockLinkActionHandler = {
|
||||
onContextMenu: function() {},
|
||||
onClick: function() {}
|
||||
};
|
||||
9
gaia/apps/sms/views/shared/test/unit/mock_link_helper.js
Executable file
9
gaia/apps/sms/views/shared/test/unit/mock_link_helper.js
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
/*exported MockLinkHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockLinkHelper = {
|
||||
searchAndLinkClickableData: function(input) {
|
||||
return input;
|
||||
}
|
||||
};
|
||||
9
gaia/apps/sms/views/shared/test/unit/mock_localization_helper.js
Executable file
9
gaia/apps/sms/views/shared/test/unit/mock_localization_helper.js
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
/*exported MockLocalizationHelper*/
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
exports.MockLocalizationHelper = {
|
||||
init: () => {}
|
||||
};
|
||||
})(window);
|
||||
82
gaia/apps/sms/views/shared/test/unit/mock_messages.js
Executable file
82
gaia/apps/sms/views/shared/test/unit/mock_messages.js
Executable file
|
|
@ -0,0 +1,82 @@
|
|||
/*exported MockMessages */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockMessages = {
|
||||
smilMockup: '<smil><head><layout>' +
|
||||
'<root-layout width="320px" height="480px"/>' +
|
||||
'<region id="Image" left="0px" top="0px" width="320px" ' +
|
||||
'height="320px" fit="meet"/><region id="Text" left="0px" ' +
|
||||
'top="320px" width="320px" height="160px" fit="meet"/>' +
|
||||
'</layout></head><body><par dur="5000ms"><img src="IMG_0011.jpg" ' +
|
||||
'region="Image"/></par></body></smil>',
|
||||
sms: function(opts = {}) {
|
||||
// default sms message
|
||||
var message = {
|
||||
id: 1,
|
||||
iccId: null,
|
||||
threadId: 1,
|
||||
sender: 'sender',
|
||||
receiver: 'receiver',
|
||||
body: 'body',
|
||||
delivery: 'received',
|
||||
deliveryStatus: 'success',
|
||||
deliveryTimestamp: Date.now(),
|
||||
type: 'sms',
|
||||
messageClass: 'normal',
|
||||
timestamp: Date.now(),
|
||||
sentTimestamp: Date.now(),
|
||||
read: true
|
||||
};
|
||||
|
||||
for (var key in message) {
|
||||
if (opts.hasOwnProperty(key)) {
|
||||
message[key] = opts[key];
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
},
|
||||
|
||||
mms: function(opts = {}) {
|
||||
var now = new Date();
|
||||
var tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
// default mms message
|
||||
var message = {
|
||||
id: 1,
|
||||
iccId: null,
|
||||
threadId: 1,
|
||||
sender: 'sender',
|
||||
receivers: ['receiver'],
|
||||
delivery: 'received',
|
||||
deliveryInfo: [{
|
||||
receiver: 'receiver',
|
||||
deliveryStatus: 'success',
|
||||
readStatus: 'success',
|
||||
deliveryTimestamp: +now,
|
||||
readTimestamp: +now,
|
||||
}],
|
||||
type: 'mms',
|
||||
timestamp: +now,
|
||||
sentTimestamp: +now,
|
||||
read: true,
|
||||
subject: '',
|
||||
smil: this.smilMockup,
|
||||
attachments: [{
|
||||
location: 'text1',
|
||||
content: new Blob(['This is a plain text'], { type: 'text/plain' })
|
||||
}],
|
||||
expiryDate: +tomorrow
|
||||
};
|
||||
|
||||
for (var key in message) {
|
||||
if (opts.hasOwnProperty(key)) {
|
||||
message[key] = opts[key];
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
};
|
||||
16
gaia/apps/sms/views/shared/test/unit/mock_mime_mapper.js
Executable file
16
gaia/apps/sms/views/shared/test/unit/mock_mime_mapper.js
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
/*exported MockMimeMapper */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockMimeMapper = {
|
||||
guessTypeFromFileProperties: function(filename, mimetype) {
|
||||
return mimetype;
|
||||
},
|
||||
|
||||
ensureFilenameMatchesType: function(filename, mimetype) {
|
||||
if (filename.indexOf('.') !== -1) {
|
||||
return filename;
|
||||
}
|
||||
return filename + '.' + 'jpg';
|
||||
}
|
||||
};
|
||||
21
gaia/apps/sms/views/shared/test/unit/mock_moz_activity.js
Executable file
21
gaia/apps/sms/views/shared/test/unit/mock_moz_activity.js
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
'use strict';
|
||||
|
||||
function MockMozActivity(activity) {
|
||||
MockMozActivity.calls.push(activity);
|
||||
MockMozActivity.instances.push(this);
|
||||
|
||||
this.onsuccess = function() {};
|
||||
this.onerror = function() {};
|
||||
}
|
||||
|
||||
// create an array to hold any calls to this function to test them
|
||||
MockMozActivity.mSetup = function() {
|
||||
MockMozActivity.calls = [];
|
||||
MockMozActivity.instances = [];
|
||||
};
|
||||
|
||||
// destroy the array so it resets every time
|
||||
MockMozActivity.mTeardown = function() {
|
||||
delete MockMozActivity.calls;
|
||||
delete MockMozActivity.instances;
|
||||
};
|
||||
8
gaia/apps/sms/views/shared/test/unit/mock_moz_alarm.js
Executable file
8
gaia/apps/sms/views/shared/test/unit/mock_moz_alarm.js
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
/* exported MockMozAlarms */
|
||||
'use strict';
|
||||
|
||||
var MockMozAlarms = {
|
||||
add: function() {},
|
||||
remove: function() {}
|
||||
};
|
||||
|
||||
19
gaia/apps/sms/views/shared/test/unit/mock_navigation.js
Executable file
19
gaia/apps/sms/views/shared/test/unit/mock_navigation.js
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
/* global
|
||||
Promise
|
||||
*/
|
||||
/* exported MockNavigation */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockNavigation = {
|
||||
back: () => Promise.resolve(),
|
||||
init: () => Promise.resolve(),
|
||||
isCurrentPanel: () => {},
|
||||
isDefaultPanel: () => false,
|
||||
toPanel: () => Promise.resolve(),
|
||||
setReady: () => {},
|
||||
on: () => {},
|
||||
off: () => {},
|
||||
once: () => {},
|
||||
hasPendingInit: () => {}
|
||||
};
|
||||
10
gaia/apps/sms/views/shared/test/unit/mock_navigator_mozphonenumberservice.js
Executable file
10
gaia/apps/sms/views/shared/test/unit/mock_navigator_mozphonenumberservice.js
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
/*exported MockMozPhoneNumberService */
|
||||
'use strict';
|
||||
|
||||
var MockMozPhoneNumberService = {
|
||||
normalize: function nmpns_normalize() {
|
||||
return true;
|
||||
},
|
||||
fuzzyMatch: function nmpns_fuzzyMatch() {
|
||||
}
|
||||
};
|
||||
5
gaia/apps/sms/views/shared/test/unit/mock_navigator_vibrate.js
Executable file
5
gaia/apps/sms/views/shared/test/unit/mock_navigator_vibrate.js
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
/*exported MockVibrate */
|
||||
|
||||
'use strict';
|
||||
|
||||
function MockVibrate() {}
|
||||
7
gaia/apps/sms/views/shared/test/unit/mock_notify.js
Executable file
7
gaia/apps/sms/views/shared/test/unit/mock_notify.js
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
/* exported MockNotify */
|
||||
'use strict';
|
||||
|
||||
var MockNotify = {
|
||||
ringtone: function() {},
|
||||
vibrate: function() {}
|
||||
};
|
||||
98
gaia/apps/sms/views/shared/test/unit/mock_recipients.js
Executable file
98
gaia/apps/sms/views/shared/test/unit/mock_recipients.js
Executable file
|
|
@ -0,0 +1,98 @@
|
|||
'use strict';
|
||||
|
||||
function MockRecipients(setup) {
|
||||
this.setup = setup;
|
||||
this.recipientsList = document.getElementById(setup.inner);
|
||||
Object.defineProperty(this, 'length', {
|
||||
get: function() {
|
||||
return this.numbersList.length;
|
||||
},
|
||||
set: function(val) {
|
||||
this.numbersList.length = val;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'numbers', {
|
||||
get: function() {
|
||||
return Array.from(this.numbersList);
|
||||
}
|
||||
});
|
||||
|
||||
this.events = {
|
||||
add: [],
|
||||
remove: [],
|
||||
modechange: []
|
||||
};
|
||||
this.numbersList = [];
|
||||
this.inputValue = '';
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
MockRecipients.prototype.add = function(contact) {
|
||||
var span = document.createElement('span');
|
||||
span.textContent = contact.number;
|
||||
this.recipientsList.appendChild(span);
|
||||
this.numbersList.push(contact.number);
|
||||
this.list.push(contact);
|
||||
this.emit('add', this.length, contact);
|
||||
return this;
|
||||
};
|
||||
|
||||
MockRecipients.prototype.remove = function(phone) {
|
||||
var index = this.numbersList.indexOf(phone);
|
||||
if (index != -1) {
|
||||
this.numbersList.splice(this.numbersList.indexOf(phone), 1);
|
||||
this.emit('remove', this.length);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
MockRecipients.prototype.render = function() {
|
||||
return this;
|
||||
};
|
||||
|
||||
MockRecipients.prototype.focus = function() {
|
||||
return this;
|
||||
};
|
||||
|
||||
MockRecipients.prototype.update = function() {
|
||||
return this;
|
||||
};
|
||||
|
||||
MockRecipients.prototype.visible = function() {
|
||||
return this;
|
||||
};
|
||||
|
||||
MockRecipients.prototype.on = function(event, callback) {
|
||||
this.events[event].push(callback);
|
||||
return this;
|
||||
};
|
||||
|
||||
MockRecipients.prototype.off = function() {
|
||||
return this;
|
||||
};
|
||||
|
||||
MockRecipients.prototype.emit = function(type) {
|
||||
var handlers = this.events;
|
||||
var args = [].slice.call(arguments, 1);
|
||||
var handler, stack;
|
||||
|
||||
if (!handlers[type]) {
|
||||
throw new Error('Invalid event type: ' + type);
|
||||
}
|
||||
|
||||
stack = handlers[type].slice();
|
||||
|
||||
if (stack.length) {
|
||||
while ((handler = stack.pop())) {
|
||||
handler.apply(null, args);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
MockRecipients.View = function() {};
|
||||
|
||||
MockRecipients.View.isFocusable = true;
|
||||
22
gaia/apps/sms/views/shared/test/unit/mock_selection_handler.js
Executable file
22
gaia/apps/sms/views/shared/test/unit/mock_selection_handler.js
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
/*exported MockSelectionHandler */
|
||||
'use strict';
|
||||
|
||||
function MockSelectionHandler(options) {
|
||||
this.selected = null;
|
||||
}
|
||||
|
||||
MockSelectionHandler.prototype = {
|
||||
get selectedCount() {
|
||||
return this.selected.size;
|
||||
},
|
||||
|
||||
get selectedList() {
|
||||
return Array.from(this.selected);
|
||||
},
|
||||
|
||||
select: function() {},
|
||||
unselect: function() {},
|
||||
toggleCheckedAll: function() {},
|
||||
cleanForm: function() {},
|
||||
allSelected: function() {}
|
||||
};
|
||||
32
gaia/apps/sms/views/shared/test/unit/mock_settings.js
Executable file
32
gaia/apps/sms/views/shared/test/unit/mock_settings.js
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var MockSettings = {
|
||||
SERVICE_ID_KEYS: {
|
||||
mmsServiceId: 'ril.mms.defaultServiceId',
|
||||
smsServiceId: 'ril.sms.defaultServiceId'
|
||||
},
|
||||
mmsSizeLimitation: 295 * 1024,
|
||||
maxConcatenatedMessages: 10,
|
||||
mmsServiceId: null,
|
||||
smsServiceId: null,
|
||||
supportEmailRecipient: false,
|
||||
init: () => {},
|
||||
switchMmsSimHandler: function() {},
|
||||
isDualSimDevice: function() { return false; },
|
||||
hasSeveralSim: function() { return false; },
|
||||
getServiceIdByIccId: function() { return null; },
|
||||
setReadAheadThreadRetrieval: function() {},
|
||||
|
||||
mSetup: function() {
|
||||
MockSettings.mmsSizeLimitation = 295 * 1024;
|
||||
MockSettings.maxConcatenatedMessages = 10;
|
||||
MockSettings.mmsServiceId = null;
|
||||
MockSettings.smsServiceId = null;
|
||||
MockSettings.supportEmailRecipient = false;
|
||||
}
|
||||
};
|
||||
|
||||
exports.MockSettings = MockSettings;
|
||||
|
||||
}(this));
|
||||
18
gaia/apps/sms/views/shared/test/unit/mock_silent_sms.js
Executable file
18
gaia/apps/sms/views/shared/test/unit/mock_silent_sms.js
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
/* global Promise */
|
||||
/* exported MockSilentSms */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockSilentSms = {
|
||||
mSilentSmsNumber: 9090,
|
||||
checkSilentModeFor: function (smsNumber) {
|
||||
return Promise.resolve(smsNumber === this.mSilentSmsNumber);
|
||||
},
|
||||
init: () => {},
|
||||
mSetup: function() {
|
||||
this.mSilentSmsNumber = 9090;
|
||||
},
|
||||
mTeardown: function() {
|
||||
this.mSilentSmsNumber = 9090;
|
||||
}
|
||||
};
|
||||
14
gaia/apps/sms/views/shared/test/unit/mock_smil.js
Executable file
14
gaia/apps/sms/views/shared/test/unit/mock_smil.js
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
/*exported MockSMIL */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockSMIL = {
|
||||
mParsed: [{'name': 'IMG_0011.jpg', 'blob': {}}],
|
||||
parse: function ms_parse(message) {
|
||||
return Promise.resolve(this.mParsed);
|
||||
},
|
||||
|
||||
generate: function ms_generate(slides) {
|
||||
return { smil: 'smil', attachments: 'attachments' };
|
||||
}
|
||||
};
|
||||
16
gaia/apps/sms/views/shared/test/unit/mock_subject_composer.js
Executable file
16
gaia/apps/sms/views/shared/test/unit/mock_subject_composer.js
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
/*exported MockSubjectComposer */
|
||||
|
||||
'use strict';
|
||||
|
||||
function MockSubjectComposer() {}
|
||||
|
||||
MockSubjectComposer.prototype.setValue = () => {};
|
||||
MockSubjectComposer.prototype.getValue = () => {};
|
||||
MockSubjectComposer.prototype.isVisible = () => {};
|
||||
MockSubjectComposer.prototype.reset = () => {};
|
||||
MockSubjectComposer.prototype.show = () => {};
|
||||
MockSubjectComposer.prototype.hide = () => {};
|
||||
MockSubjectComposer.prototype.toggle = () => {};
|
||||
MockSubjectComposer.prototype.focus = () => {};
|
||||
MockSubjectComposer.prototype.getMaxLength = () => {};
|
||||
MockSubjectComposer.prototype.on = () => {};
|
||||
6
gaia/apps/sms/views/shared/test/unit/mock_system_message_handler.js
Executable file
6
gaia/apps/sms/views/shared/test/unit/mock_system_message_handler.js
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
/*exported MockSystemMessageHandler */
|
||||
'use strict';
|
||||
|
||||
var MockSystemMessageHandler = {
|
||||
init() {}
|
||||
};
|
||||
11
gaia/apps/sms/views/shared/test/unit/mock_time_headers.js
Executable file
11
gaia/apps/sms/views/shared/test/unit/mock_time_headers.js
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
/*exported MockTimeHeaders */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockTimeHeaders = {
|
||||
init: function() {},
|
||||
startScheduler: function() {},
|
||||
stopScheduler: function() {},
|
||||
update: function() {},
|
||||
updateAll: function() {}
|
||||
};
|
||||
10
gaia/apps/sms/views/shared/test/unit/mock_url.js
Executable file
10
gaia/apps/sms/views/shared/test/unit/mock_url.js
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
/*exported MockURL */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockURL = {
|
||||
createObjectURL: function(url) {
|
||||
return url;
|
||||
},
|
||||
revokeObjectURL: function(url) {}
|
||||
};
|
||||
41
gaia/apps/sms/views/shared/test/unit/mock_utils.js
Executable file
41
gaia/apps/sms/views/shared/test/unit/mock_utils.js
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
/* global Utils, Promise */
|
||||
/* exported MockUtils */
|
||||
|
||||
var MockUtils = {
|
||||
// we need that this function does real work, so it's copied from the real
|
||||
// Utils.js
|
||||
camelCase: Utils.camelCase,
|
||||
date: Utils.date,
|
||||
getDayDate: Utils.getDayDate,
|
||||
setHeaderDate: Utils.setHeaderDate,
|
||||
getFormattedHour: Utils.getFormattedHour,
|
||||
_getFormatter: Utils._getFormatter,
|
||||
// real code needed here to map types
|
||||
typeFromMimeType: Utils.typeFromMimeType,
|
||||
escapeRegex: Utils.escapeRegex,
|
||||
params: Utils.params,
|
||||
url: Utils.url,
|
||||
getContactDetails: Utils.getContactDetails,
|
||||
getResizedImgBlob: Utils.getResizedImgBlob,
|
||||
getSizeForL10n: Utils.getSizeForL10n,
|
||||
getPhoneDetails: Utils.getPhoneDetails,
|
||||
removeNonDialables: Utils.removeNonDialables,
|
||||
multiRecipientMatch: Utils.multiRecipientMatch,
|
||||
probablyMatches: Utils.probablyMatches,
|
||||
getDisplayObject: Utils.getDisplayObject,
|
||||
basicContact: Utils.basicContact,
|
||||
asyncLoadRevokeURL: Utils.asyncLoadRevokeURL,
|
||||
isEmailAddress: Utils.isEmailAddress,
|
||||
closeNotificationsForThread: () => Promise.resolve(),
|
||||
imageToDataUrl: Utils.imageToDataUrl,
|
||||
debounce: Utils.debounce,
|
||||
throttle: Utils.throttle,
|
||||
alert: Utils.alert,
|
||||
confirm: Utils.confirm,
|
||||
Promise: Utils.Promise,
|
||||
getSimNameByIccId: Utils.getSimNameByIccId,
|
||||
initializeShimHost: () => Promise.resolve(),
|
||||
onceDocumentIsVisible: Utils.onceDocumentIsVisible
|
||||
};
|
||||
8
gaia/apps/sms/views/shared/test/unit/mock_waiting_screen.js
Executable file
8
gaia/apps/sms/views/shared/test/unit/mock_waiting_screen.js
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
/*exported MockWaitingScreen */
|
||||
|
||||
'use strict';
|
||||
|
||||
var MockWaitingScreen = {
|
||||
show: function() {},
|
||||
hide: function() {}
|
||||
};
|
||||
966
gaia/apps/sms/views/shared/test/unit/navigation_test.js
Executable file
966
gaia/apps/sms/views/shared/test/unit/navigation_test.js
Executable file
|
|
@ -0,0 +1,966 @@
|
|||
/* global
|
||||
AnimationEvent,
|
||||
HashChangeEvent,
|
||||
MocksHelper,
|
||||
Navigation,
|
||||
NavigationFactory,
|
||||
Promise
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/event_dispatcher.js');
|
||||
|
||||
require('/views/shared/js/utils.js');
|
||||
require('/views/shared/test/unit/mock_utils.js');
|
||||
|
||||
require('/views/shared/js/navigation.js');
|
||||
|
||||
var mocksHelperForNavigation = new MocksHelper([
|
||||
'Utils'
|
||||
]).init();
|
||||
|
||||
suite('navigation >', function() {
|
||||
var FAKE_VIEW_OBJECTS =
|
||||
['InboxView', 'ConversationView', 'ReportView', 'GroupView'];
|
||||
|
||||
var fakeWindow, fakeLocation;
|
||||
var elements;
|
||||
|
||||
mocksHelperForNavigation.attachTestHelpers();
|
||||
|
||||
function fakeViewObjects() {
|
||||
FAKE_VIEW_OBJECTS.forEach((objectName) => {
|
||||
fakeWindow[objectName] = {
|
||||
beforeLeave: sinon.stub(),
|
||||
beforeEnter: sinon.stub(),
|
||||
afterLeave: sinon.stub(),
|
||||
afterEnter: sinon.stub()
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function resetViewObjectStubs() {
|
||||
FAKE_VIEW_OBJECTS.map(
|
||||
(objectName) => fakeWindow[objectName]
|
||||
).forEach((viewObject) => {
|
||||
['beforeLeave', 'beforeEnter', 'afterLeave', 'afterEnter'].forEach(
|
||||
(stepName) => viewObject[stepName].reset()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function fakeAssign(url) {
|
||||
// hashchange event should not be fired if hash does not change.
|
||||
if (url.startsWith('#') && url !== fakeLocation.hash) {
|
||||
setFakeLocation({ hash: url });
|
||||
Promise.resolve().then(
|
||||
() => window.dispatchEvent(new HashChangeEvent('hashchange'))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function setFakeLocation({ pathname, hash }) {
|
||||
if (pathname !== undefined) {
|
||||
fakeLocation.pathname = pathname;
|
||||
}
|
||||
|
||||
if (hash !== undefined) {
|
||||
fakeLocation.hash = hash;
|
||||
}
|
||||
|
||||
fakeLocation.href =
|
||||
'app://sms.gaiamobile.org' + fakeLocation.pathname + fakeLocation.hash;
|
||||
}
|
||||
|
||||
function findViewElements() {
|
||||
elements = {};
|
||||
FAKE_VIEW_OBJECTS.forEach((viewName) => {
|
||||
elements[viewName] = document.querySelector('.panel-' + viewName);
|
||||
});
|
||||
}
|
||||
|
||||
function styleDescriptor(setterStub, propSetterStubs) {
|
||||
return {
|
||||
get: function() {
|
||||
var result = {};
|
||||
var descriptor = {};
|
||||
|
||||
for (var prop in propSetterStubs) {
|
||||
descriptor[prop] = { set: propSetterStubs[prop] };
|
||||
}
|
||||
|
||||
Object.defineProperties(result, descriptor);
|
||||
return result;
|
||||
},
|
||||
set: setterStub,
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
};
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
this.sinon.stub(window, 'setTimeout').yieldsAsync();
|
||||
loadBodyHTML('/index.html');
|
||||
navigator.mozHasPendingMessage = sinon.stub();
|
||||
|
||||
fakeLocation = {
|
||||
assign: sinon.spy(fakeAssign),
|
||||
hash: '',
|
||||
href: 'app://sms.gaiamobile.org/',
|
||||
pathname: '/' // old architecture
|
||||
};
|
||||
|
||||
fakeWindow = Object.create(window, {
|
||||
location: {
|
||||
get: () => fakeLocation
|
||||
},
|
||||
addEventListener: {
|
||||
get: () => (...args) => window.addEventListener(...args)
|
||||
},
|
||||
removeEventListener: {
|
||||
get: () => (...args) => window.removeEventListener(...args)
|
||||
},
|
||||
history: {
|
||||
value: {
|
||||
back: () => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
findViewElements();
|
||||
fakeViewObjects();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
Navigation.cleanup();
|
||||
fakeWindow = null;
|
||||
elements = null;
|
||||
window.Navigation = null;
|
||||
document.body.innerHTML = '';
|
||||
delete navigator.mozHasPendingMessage;
|
||||
});
|
||||
|
||||
suite('non split views >', function() {
|
||||
setup(function() {
|
||||
window.Navigation = NavigationFactory(fakeWindow);
|
||||
navigator.mozHasPendingMessage.withArgs('notification').returns(false);
|
||||
});
|
||||
|
||||
suite('init() >', function() {
|
||||
var panel;
|
||||
setup(function() {
|
||||
panel = document.querySelector('.panel-InboxView');
|
||||
});
|
||||
|
||||
test('display the right panel', function(done) {
|
||||
assert.isFalse(elements.InboxView.classList.contains('panel-active'));
|
||||
assert.isTrue(elements.InboxView.classList.contains('panel-hidden'));
|
||||
assert.equal(elements.InboxView.getAttribute('aria-hidden'), 'true');
|
||||
Navigation.init().then(() => {
|
||||
assert.isTrue(elements.InboxView.classList.contains('panel-active'));
|
||||
assert.isFalse(elements.InboxView.classList.contains('panel-hidden'));
|
||||
assert.equal(elements.InboxView.getAttribute('aria-hidden'), 'false');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('runs lifecycle methods for the right view', function(done) {
|
||||
setFakeLocation({ hash: '#/composer?id=3' });
|
||||
|
||||
var transitionArgs = { id: '3' };
|
||||
Navigation.init().then(() => {
|
||||
sinon.assert.calledWithMatch(
|
||||
fakeWindow.ConversationView.beforeEnter,
|
||||
transitionArgs
|
||||
);
|
||||
sinon.assert.calledWithMatch(
|
||||
fakeWindow.ConversationView.afterEnter,
|
||||
transitionArgs
|
||||
);
|
||||
assert.ok(Navigation.isCurrentPanel('composer', { id: 3 }));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Do nothing when an unknown view is asked (hash)', function(done) {
|
||||
this.sinon.stub(console, 'error');
|
||||
|
||||
setFakeLocation({ hash: '#/unknown?id=3' });
|
||||
|
||||
Navigation.init().catch(() => {}).then(() => {
|
||||
sinon.assert.notCalled(fakeWindow.InboxView.beforeEnter);
|
||||
sinon.assert.notCalled(fakeWindow.InboxView.afterEnter);
|
||||
assert.isFalse(Navigation.isCurrentPanel('thread-list'));
|
||||
assert.isFalse(elements.InboxView.classList.contains('panel-active'));
|
||||
assert.equal(elements.InboxView.getAttribute('aria-hidden'), 'true');
|
||||
sinon.assert.calledTwice(console.error);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Set init pending when launched by notification', function(done) {
|
||||
this.sinon.stub(console, 'error');
|
||||
navigator.mozHasPendingMessage.withArgs('notification').returns(true);
|
||||
|
||||
assert.isFalse(Navigation.hasPendingInit());
|
||||
Navigation.init().catch(() => {}).then(() => {
|
||||
assert.isTrue(Navigation.hasPendingInit());
|
||||
sinon.assert.notCalled(fakeWindow.InboxView.beforeEnter);
|
||||
sinon.assert.notCalled(fakeWindow.InboxView.afterEnter);
|
||||
assert.isFalse(Navigation.isCurrentPanel('thread-list'));
|
||||
assert.isFalse(elements.InboxView.classList.contains('panel-active'));
|
||||
assert.equal(elements.InboxView.getAttribute('aria-hidden'), 'true');
|
||||
sinon.assert.notCalled(console.error);
|
||||
|
||||
// Navigate to composer panel
|
||||
return Navigation.toPanel('composer');
|
||||
}).then(() => {
|
||||
assert.isFalse(Navigation.hasPendingInit());
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Initial navigation request should proceed to the completion ' +
|
||||
'without waiting for the document to be completely loaded',
|
||||
function(done) {
|
||||
Object.defineProperty(document, 'readyState', { value: 'interactive' });
|
||||
|
||||
Navigation.init().then(done, done);
|
||||
});
|
||||
|
||||
test('Initial panel should fade-in', function(done) {
|
||||
var styleSetterStub = sinon.stub();
|
||||
var styleAnimationNameStub = sinon.stub();
|
||||
Object.defineProperty(
|
||||
panel, 'style',
|
||||
styleDescriptor(
|
||||
styleSetterStub, { animationName: styleAnimationNameStub }
|
||||
)
|
||||
);
|
||||
|
||||
Navigation.init().then(() => {
|
||||
sinon.assert.calledWith(styleAnimationNameStub, 'fade-in');
|
||||
sinon.assert.calledWith(styleSetterStub, '');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('back() >', function() {
|
||||
setup(function(done) {
|
||||
Navigation.init().then(done, done);
|
||||
});
|
||||
|
||||
test('Properly returns back in history', function(done) {
|
||||
this.sinon.stub(fakeWindow.history, 'back', () => {
|
||||
fakeAssign('#/composer');
|
||||
});
|
||||
|
||||
Navigation.back().then(() => {
|
||||
sinon.assert.called(fakeWindow.InboxView.beforeLeave);
|
||||
sinon.assert.called(fakeWindow.history.back);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('toPanel >', function() {
|
||||
setup(function(done) {
|
||||
Navigation.init().then(done, done);
|
||||
});
|
||||
|
||||
test('Return a rejected promise if the panel is unknown', function(done) {
|
||||
Navigation.toPanel('unknown').then(
|
||||
function onresolve() {
|
||||
done(new Error('should not return a resolved promise'));
|
||||
}, function onreject() {
|
||||
// we don't pass "done" directly because we don't want that done
|
||||
// gets an argument as it interprets this as a failure
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('Return a rejected promise if the panel is badly configured',
|
||||
function(done) {
|
||||
Navigation.toPanel('bad-panel').then(
|
||||
function onresolve() {
|
||||
done(new Error('should not return a resolved promise'));
|
||||
}, function onreject() {
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('Queue panel requests while not ready', function(done) {
|
||||
var fakeInbox = fakeWindow.InboxView;
|
||||
var fakeConversation = fakeWindow.ConversationView;
|
||||
fakeWindow.InboxView = null;
|
||||
fakeWindow.ConversationView = null;
|
||||
|
||||
Navigation.toPanel('composer');
|
||||
Navigation.toPanel('thread', { id: 1 }).then(function() {
|
||||
sinon.assert.callOrder(
|
||||
fakeWindow.InboxView.beforeLeave,
|
||||
fakeWindow.ConversationView.beforeEnter,
|
||||
fakeWindow.InboxView.afterLeave,
|
||||
fakeWindow.ConversationView.afterEnter
|
||||
);
|
||||
}).then(done, done);
|
||||
|
||||
setTimeout(() => {
|
||||
fakeWindow.InboxView = fakeInbox;
|
||||
fakeWindow.ConversationView = fakeConversation;
|
||||
Navigation.setReady();
|
||||
});
|
||||
});
|
||||
|
||||
test('Queue panel transition requests', function(done) {
|
||||
Navigation.toPanel('thread-list');
|
||||
Navigation.toPanel('composer').then(function() {
|
||||
sinon.assert.callOrder(
|
||||
fakeWindow.InboxView.beforeEnter,
|
||||
fakeWindow.InboxView.afterEnter,
|
||||
fakeWindow.ConversationView.beforeEnter,
|
||||
fakeWindow.ConversationView.afterEnter
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Queued panel also forwards rejections', function(done) {
|
||||
fakeWindow.ConversationView.beforeLeave.throws(
|
||||
new Error('error while entering ReportView')
|
||||
);
|
||||
|
||||
var req1 = Navigation.toPanel('thread', {id: 1}).then(
|
||||
() => 'resolved', () => 'rejected'
|
||||
);
|
||||
var req2 = Navigation.toPanel('report-view', {id: 1}).then(
|
||||
() => 'resolved', () => 'rejected'
|
||||
);
|
||||
Promise.all([req1, req2]).then(function(results) {
|
||||
assert.deepEqual(results, [ 'resolved', 'rejected' ]);
|
||||
|
||||
sinon.assert.callOrder(
|
||||
fakeWindow.ConversationView.beforeEnter,
|
||||
fakeWindow.ConversationView.afterEnter,
|
||||
fakeWindow.ConversationView.beforeLeave
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Transition should resolve correctly while requesting same panel',
|
||||
function(done) {
|
||||
|
||||
Navigation.toPanel('thread', { id: 1 });
|
||||
Navigation.toPanel('thread', { id: 1 }).then(() =>
|
||||
Navigation.toPanel('thread-list')
|
||||
).then(() => {
|
||||
sinon.assert.callOrder(
|
||||
fakeWindow.ConversationView.beforeEnter,
|
||||
fakeWindow.ConversationView.afterEnter,
|
||||
fakeWindow.InboxView.beforeEnter,
|
||||
fakeWindow.InboxView.afterEnter
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
suite('lifecycle >', function() {
|
||||
var panel1, panel2, panelName1, panelName2, panelName3;
|
||||
setup(function(done) {
|
||||
panel1 = fakeWindow.ConversationView;
|
||||
panel2 = fakeWindow.ReportView;
|
||||
panelName1 = 'thread';
|
||||
panelName2 = 'report-view';
|
||||
panelName3 = 'thread-list';
|
||||
|
||||
Navigation.toPanel(panelName1).then(function() {
|
||||
// reseting lifecycle functions because we want to test the next
|
||||
// transition
|
||||
resetViewObjectStubs();
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('lifecycle order', function(done) {
|
||||
var onNavigatedStub = sinon.stub();
|
||||
Navigation.on('navigated', onNavigatedStub);
|
||||
Navigation.toPanel(panelName2).then(function() {
|
||||
sinon.assert.callOrder(
|
||||
panel1.beforeLeave,
|
||||
panel2.beforeEnter,
|
||||
panel2.afterEnter,
|
||||
panel1.afterLeave,
|
||||
onNavigatedStub
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('isCurrentPanel is changed at the right time', function(done) {
|
||||
var results = [];
|
||||
|
||||
panel1.beforeLeave = function() {
|
||||
results.push(Navigation.isCurrentPanel(panelName1));
|
||||
};
|
||||
panel1.afterLeave = function() {
|
||||
results.push(Navigation.isCurrentPanel(panelName2));
|
||||
};
|
||||
panel2.beforeEnter = function() {
|
||||
results.push(Navigation.isCurrentPanel(panelName1));
|
||||
};
|
||||
panel2.afterEnter = function() {
|
||||
results.push(Navigation.isCurrentPanel(panelName2));
|
||||
};
|
||||
|
||||
Navigation.toPanel(panelName2).then(function() {
|
||||
assert.equal(results.length, 4);
|
||||
results.forEach(function(val, i) {
|
||||
assert.ok(val, 'result ' + i);
|
||||
});
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('lifecycle methods get passed the correct args', function(done) {
|
||||
var expectedArgs = {
|
||||
id: sinon.match(1),
|
||||
meta: {
|
||||
next: {
|
||||
panel: panelName2,
|
||||
args: sinon.match({
|
||||
id: sinon.match(1)
|
||||
})
|
||||
},
|
||||
prev: {
|
||||
panel: panelName1,
|
||||
args: sinon.match.object
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Navigation.toPanel(panelName2, { id: 1 }).then(function() {
|
||||
sinon.assert.calledWithMatch(panel1.beforeLeave, expectedArgs);
|
||||
sinon.assert.calledWithMatch(panel1.afterLeave, expectedArgs);
|
||||
sinon.assert.calledWithMatch(panel2.beforeEnter, expectedArgs);
|
||||
sinon.assert.calledWithMatch(panel2.afterEnter, expectedArgs);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
suite('beforeLeave failures >', function() {
|
||||
test('throwing', function(done) {
|
||||
panel1.beforeLeave.onFirstCall().throws();
|
||||
|
||||
Navigation.toPanel(panelName2).then(function resolved_unexpected() {
|
||||
throw new Error('toPanel should not be resolved');
|
||||
}, function rejected_expected() {
|
||||
sinon.assert.notCalled(panel2.beforeEnter);
|
||||
sinon.assert.notCalled(panel1.afterLeave);
|
||||
sinon.assert.notCalled(panel2.afterEnter);
|
||||
assert.ok(Navigation.isCurrentPanel(panelName1));
|
||||
|
||||
// check we can have a new transition
|
||||
return Navigation.toPanel(panelName3);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('returning a rejected promise', function(done) {
|
||||
panel1.beforeLeave.onFirstCall().returns(Promise.reject());
|
||||
|
||||
Navigation.toPanel(panelName2).then(function resolved_unexpected() {
|
||||
throw new Error('toPanel should not be resolved');
|
||||
}, function rejected_expected() {
|
||||
sinon.assert.notCalled(panel2.beforeEnter);
|
||||
sinon.assert.notCalled(panel1.afterLeave);
|
||||
sinon.assert.notCalled(panel2.afterEnter);
|
||||
assert.ok(Navigation.isCurrentPanel(panelName1));
|
||||
|
||||
// check we can have a new transition
|
||||
return Navigation.toPanel(panelName3);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('beforeEnter failures >', function() {
|
||||
test('throwing', function(done) {
|
||||
panel2.beforeEnter.onFirstCall().throws();
|
||||
|
||||
Navigation.toPanel(panelName2).then(function resolved_expected() {
|
||||
sinon.assert.callOrder(
|
||||
panel1.beforeLeave,
|
||||
panel2.beforeEnter,
|
||||
panel2.afterEnter,
|
||||
panel1.afterLeave
|
||||
);
|
||||
assert.ok(Navigation.isCurrentPanel(panelName2));
|
||||
|
||||
// check we can have a new transition
|
||||
return Navigation.toPanel(panelName3);
|
||||
}, function rejected_unexpected() {
|
||||
throw new Error('toPanel should not be rejected');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('returning a rejected promise', function(done) {
|
||||
panel2.beforeEnter.onFirstCall().returns(Promise.reject());
|
||||
|
||||
Navigation.toPanel(panelName2).then(function resolved_expected() {
|
||||
sinon.assert.callOrder(
|
||||
panel1.beforeLeave,
|
||||
panel2.beforeEnter,
|
||||
panel2.afterEnter,
|
||||
panel1.afterLeave
|
||||
);
|
||||
assert.ok(Navigation.isCurrentPanel(panelName2));
|
||||
|
||||
// check we can have a new transition
|
||||
return Navigation.toPanel(panelName3);
|
||||
}, function rejected_unexpected() {
|
||||
throw new Error('toPanel should not be rejected');
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('afterLeave failures >', function() {
|
||||
test('throwing', function(done) {
|
||||
panel1.afterLeave.onFirstCall().throws();
|
||||
|
||||
Navigation.toPanel(panelName2).then(function resolved_expected() {
|
||||
sinon.assert.called(panel2.afterEnter);
|
||||
assert.ok(Navigation.isCurrentPanel(panelName2));
|
||||
|
||||
// check we can have a new transition
|
||||
return Navigation.toPanel(panelName3);
|
||||
}, function rejected_unexpected() {
|
||||
throw new Error('toPanel should not be rejected');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('returning a rejected promise', function(done) {
|
||||
panel1.afterLeave.onFirstCall().returns(Promise.reject());
|
||||
|
||||
Navigation.toPanel(panelName2).then(function resolved_expected() {
|
||||
sinon.assert.called(panel2.afterEnter);
|
||||
assert.ok(Navigation.isCurrentPanel(panelName2));
|
||||
|
||||
// check we can have a new transition
|
||||
return Navigation.toPanel(panelName3);
|
||||
}, function rejected_unexpected() {
|
||||
throw new Error('toPanel should not be rejected');
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('afterEnter failures >', function() {
|
||||
test('throwing', function(done) {
|
||||
panel2.afterEnter.onFirstCall().throws();
|
||||
this.sinon.stub(console, 'error');
|
||||
|
||||
Navigation.toPanel(panelName2).then(function resolved_expected() {
|
||||
assert.ok(Navigation.isCurrentPanel(panelName2));
|
||||
sinon.assert.called(console.error);
|
||||
|
||||
// check we can have a new transition
|
||||
return Navigation.toPanel(panelName3);
|
||||
}, function rejected_unexpected() {
|
||||
throw new Error('toPanel should not be rejected');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('returning a rejected promise', function(done) {
|
||||
panel2.afterEnter.onFirstCall().returns(Promise.reject());
|
||||
this.sinon.stub(console, 'log');
|
||||
|
||||
Navigation.toPanel(panelName2).then(function resolved_expected() {
|
||||
assert.ok(Navigation.isCurrentPanel(panelName2));
|
||||
|
||||
// check we can have a new transition
|
||||
return Navigation.toPanel(panelName3);
|
||||
}, function rejected_unexpected() {
|
||||
throw new Error('toPanel should not be rejected');
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Remove any focus left on specific elements ', function() {
|
||||
this.sinon.spy(document.activeElement, 'blur');
|
||||
Navigation.toPanel('thread');
|
||||
sinon.assert.called(document.activeElement.blur);
|
||||
});
|
||||
});
|
||||
|
||||
suite('URL manipulation >', function() {
|
||||
setup(function(done) {
|
||||
Navigation.init().then(done, done);
|
||||
});
|
||||
|
||||
test('toPanel changes the location', function(done) {
|
||||
Navigation.toPanel('composer', { id: 3 }).then(() => {
|
||||
sinon.assert.calledWith(
|
||||
fakeWindow.location.assign,
|
||||
'#/composer?id=3'
|
||||
);
|
||||
|
||||
return Navigation.toPanel('thread-list');
|
||||
}).then(() => {
|
||||
sinon.assert.calledWith(
|
||||
fakeWindow.location.assign,
|
||||
'#'
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
test('isDefaultPanel() correctly determines default panel', function() {
|
||||
// If hash is empty.
|
||||
setFakeLocation({ hash: '' });
|
||||
assert.isTrue(Navigation.isDefaultPanel());
|
||||
|
||||
// False if hash set to some different panel.
|
||||
setFakeLocation({ hash: '#/composer' });
|
||||
assert.isFalse(Navigation.isDefaultPanel());
|
||||
});
|
||||
|
||||
suite('isCurrentPanel()', function() {
|
||||
var view1 = 'thread-list';
|
||||
var view2 = 'thread';
|
||||
|
||||
setup(function(done) {
|
||||
Navigation.init().then(done, done);
|
||||
});
|
||||
|
||||
test('returns false if the argument is falsy', function() {
|
||||
assert.isFalse(Navigation.isCurrentPanel(null));
|
||||
assert.isFalse(Navigation.isCurrentPanel());
|
||||
});
|
||||
|
||||
test('returns the correct value without args', function() {
|
||||
assert.isTrue(Navigation.isCurrentPanel(view1));
|
||||
assert.isFalse(Navigation.isCurrentPanel(view2));
|
||||
assert.isFalse(Navigation.isCurrentPanel(view1, { prop: 1 }));
|
||||
});
|
||||
|
||||
test('returns the correct value with args', function(done) {
|
||||
Navigation.toPanel(view2, { prop1: 1, prop2: 'prop2' })
|
||||
.then(function() {
|
||||
assert.isTrue(Navigation.isCurrentPanel(view2));
|
||||
assert.isTrue(Navigation.isCurrentPanel(view2, { prop1: 1 }));
|
||||
assert.isTrue(Navigation.isCurrentPanel(view2, { prop2: 'prop2' }));
|
||||
assert.isTrue(
|
||||
Navigation.isCurrentPanel(view2, { prop1: 1, prop2: 'prop2' })
|
||||
);
|
||||
|
||||
assert.isFalse(Navigation.isCurrentPanel(view1));
|
||||
assert.isFalse(Navigation.isCurrentPanel(view2, { prop1: 2 }));
|
||||
assert.isFalse(
|
||||
Navigation.isCurrentPanel(view2, { prop1: 1, prop2: 'prop1' })
|
||||
);
|
||||
assert.isFalse(
|
||||
Navigation.isCurrentPanel(view2, { prop2: 'prop1' })
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('sliding views,', function() {
|
||||
setup(function(done) {
|
||||
Navigation.init().then(done, done);
|
||||
});
|
||||
|
||||
function animationEndEvent() {
|
||||
return new AnimationEvent('animationend', {
|
||||
bubbles: true,
|
||||
animationName: 'some-name'
|
||||
});
|
||||
}
|
||||
|
||||
test('resolves the promise after animationend events', function(done) {
|
||||
// neutralizes the setTimeout fallback
|
||||
window.setTimeout.restore();
|
||||
|
||||
var panel = document.querySelector('.panel-ConversationView');
|
||||
this.sinon.spy(panel, 'removeEventListener');
|
||||
setTimeout(
|
||||
() => panel.dispatchEvent(animationEndEvent())
|
||||
);
|
||||
this.sinon.stub(window, 'setTimeout').returns(42);
|
||||
this.sinon.stub(window, 'clearTimeout');
|
||||
|
||||
Navigation.toPanel('thread').then(() => {
|
||||
sinon.assert.calledWith(panel.removeEventListener, 'animationend');
|
||||
sinon.assert.called(window.setTimeout);
|
||||
sinon.assert.calledWith(window.clearTimeout, 42);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
suite('animations,', function() {
|
||||
var panel1, panel2;
|
||||
var styleStubs;
|
||||
var styleAnimationNameStubs;
|
||||
|
||||
setup(function() {
|
||||
panel1 = document.querySelector('.panel-InboxView');
|
||||
panel2 = document.querySelector('.panel-ConversationView');
|
||||
|
||||
styleStubs = [];
|
||||
styleAnimationNameStubs = [];
|
||||
|
||||
[panel1, panel2].forEach((panel, i) => {
|
||||
styleStubs[i] = sinon.stub();
|
||||
styleAnimationNameStubs[i] = sinon.stub();
|
||||
|
||||
Object.defineProperty(
|
||||
panel, 'style',
|
||||
styleDescriptor(
|
||||
styleStubs[i], { animationName: styleAnimationNameStubs[i] }
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
document.dir = '';
|
||||
});
|
||||
|
||||
test('sliding forwards in LTR', function(done) {
|
||||
Navigation.toPanel('thread').then(() => {
|
||||
// panel1 is the old panel, panel2 is the new panel
|
||||
sinon.assert.calledWith(
|
||||
styleAnimationNameStubs[0], 'current-to-left'
|
||||
);
|
||||
sinon.assert.calledWith(
|
||||
styleAnimationNameStubs[1], 'right-to-current'
|
||||
);
|
||||
sinon.assert.calledWith(styleStubs[0], '');
|
||||
sinon.assert.calledWith(styleStubs[1], '');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('sliding forwards in RTL', function(done) {
|
||||
document.dir = 'rtl';
|
||||
Navigation.toPanel('thread').then(() => {
|
||||
// panel1 is the old panel, panel2 is the new panel
|
||||
sinon.assert.calledWith(
|
||||
styleAnimationNameStubs[0], 'current-to-right'
|
||||
);
|
||||
sinon.assert.calledWith(
|
||||
styleAnimationNameStubs[1], 'left-to-current'
|
||||
);
|
||||
sinon.assert.calledWith(styleStubs[0], '');
|
||||
sinon.assert.calledWith(styleStubs[1], '');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
suite('sliding backwards', function() {
|
||||
setup(function(done) {
|
||||
// First going to the conversation view, and resetting the stubs
|
||||
Navigation.toPanel('thread').then(() => {
|
||||
styleStubs.forEach((stub) => stub.reset());
|
||||
styleAnimationNameStubs.forEach((stub) => stub.reset());
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('in LTR', function(done) {
|
||||
Navigation.toPanel('thread-list').then(() => {
|
||||
// panel1 is the new panel, panel2 is the old panel
|
||||
sinon.assert.calledWith(
|
||||
styleAnimationNameStubs[0], 'left-to-current'
|
||||
);
|
||||
sinon.assert.calledWith(
|
||||
styleAnimationNameStubs[1], 'current-to-right'
|
||||
);
|
||||
sinon.assert.calledWith(styleStubs[0], '');
|
||||
sinon.assert.calledWith(styleStubs[1], '');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('in RTL', function(done) {
|
||||
document.dir = 'rtl';
|
||||
|
||||
Navigation.toPanel('thread-list').then(() => {
|
||||
// panel1 is the new panel, panel2 is the old panel
|
||||
sinon.assert.calledWith(
|
||||
styleAnimationNameStubs[0], 'right-to-current'
|
||||
);
|
||||
sinon.assert.calledWith(
|
||||
styleAnimationNameStubs[1], 'current-to-left'
|
||||
);
|
||||
sinon.assert.calledWith(styleStubs[0], '');
|
||||
sinon.assert.calledWith(styleStubs[1], '');
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('setReady', function() {
|
||||
|
||||
test('Remove hidden class once the navigation is ready', function() {
|
||||
|
||||
assert.lengthOf(document.querySelectorAll('.panel-hidden'), 4);
|
||||
|
||||
Navigation.setReady();
|
||||
|
||||
assert.lengthOf(document.querySelectorAll('.panel-hidden'), 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('split views >', function() {
|
||||
setup(function() {
|
||||
setFakeLocation({ pathname: '/views/inbox/' });
|
||||
|
||||
window.Navigation = NavigationFactory(fakeWindow);
|
||||
navigator.mozHasPendingMessage.withArgs('notification').returns(false);
|
||||
|
||||
Navigation.setReady();
|
||||
});
|
||||
|
||||
suite('init()) >', function() {
|
||||
test('display the right panel', function(done) {
|
||||
assert.isFalse(elements.InboxView.classList.contains('panel-active'));
|
||||
assert.equal(elements.InboxView.getAttribute('aria-hidden'), 'true');
|
||||
Navigation.init().then(() => {
|
||||
assert.ok(elements.InboxView.classList.contains('panel-active'));
|
||||
assert.equal(elements.InboxView.getAttribute('aria-hidden'), 'false');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('find the right split view', function(done) {
|
||||
setFakeLocation({ pathname: '/views/conversation/', hash: '#?id=3'});
|
||||
|
||||
var transitionArgs = { id: '3' };
|
||||
Navigation.init().then(() => {
|
||||
sinon.assert.calledWithMatch(
|
||||
fakeWindow.ConversationView.beforeEnter,
|
||||
transitionArgs
|
||||
);
|
||||
sinon.assert.calledWithMatch(
|
||||
fakeWindow.ConversationView.afterEnter,
|
||||
transitionArgs
|
||||
);
|
||||
assert.ok(Navigation.isCurrentPanel('thread', { id: 3 }));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('find the right split subview', function(done) {
|
||||
setFakeLocation(
|
||||
{ pathname: '/views/conversation/', hash: '#/report-view?id=3' }
|
||||
);
|
||||
|
||||
var transitionArgs = { id: '3' };
|
||||
Navigation.init().then(() => {
|
||||
sinon.assert.calledWithMatch(
|
||||
fakeWindow.ReportView.beforeEnter,
|
||||
transitionArgs
|
||||
);
|
||||
sinon.assert.calledWithMatch(
|
||||
fakeWindow.ReportView.afterEnter,
|
||||
transitionArgs
|
||||
);
|
||||
assert.ok(Navigation.isCurrentPanel('report-view', { id: 3 }));
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Do nothing when an unknown view is asked (hash)', function(done) {
|
||||
this.sinon.stub(console, 'error');
|
||||
|
||||
setFakeLocation({ hash: '#/unknown?id=3' });
|
||||
|
||||
Navigation.init().catch(() => {}).then(() => {
|
||||
sinon.assert.notCalled(fakeWindow.InboxView.beforeEnter);
|
||||
sinon.assert.notCalled(fakeWindow.InboxView.afterEnter);
|
||||
assert.isFalse(Navigation.isCurrentPanel('thread-list'));
|
||||
assert.isFalse(elements.InboxView.classList.contains('panel-active'));
|
||||
assert.equal(elements.InboxView.getAttribute('aria-hidden'), 'true');
|
||||
sinon.assert.calledTwice(console.error);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Do nothing when an unknown view is asked (path)', function(done) {
|
||||
this.sinon.stub(console, 'error');
|
||||
|
||||
setFakeLocation({ pathname: '/unknown/' });
|
||||
|
||||
Navigation.init().catch(() => {}).then(() => {
|
||||
sinon.assert.notCalled(fakeWindow.InboxView.beforeEnter);
|
||||
sinon.assert.notCalled(fakeWindow.InboxView.afterEnter);
|
||||
assert.isFalse(Navigation.isCurrentPanel('thread-list'));
|
||||
assert.isFalse(elements.InboxView.classList.contains('panel-active'));
|
||||
assert.equal(elements.InboxView.getAttribute('aria-hidden'), 'true');
|
||||
sinon.assert.calledTwice(console.error);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Load the view from path when an unrelated subview is asked',
|
||||
function(done) {
|
||||
this.sinon.stub(console, 'error');
|
||||
|
||||
setFakeLocation({ pathname: '/views/inbox/', hash: '#/composer?id=3' });
|
||||
|
||||
Navigation.init().then(() => {
|
||||
sinon.assert.called(fakeWindow.InboxView.beforeEnter);
|
||||
sinon.assert.called(fakeWindow.InboxView.afterEnter);
|
||||
assert.isTrue(Navigation.isCurrentPanel('thread-list'));
|
||||
assert.isTrue(elements.InboxView.classList.contains('panel-active'));
|
||||
assert.equal(elements.InboxView.getAttribute('aria-hidden'), 'false');
|
||||
sinon.assert.calledOnce(console.error);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Set init pending when launched by notification', function(done) {
|
||||
this.sinon.stub(console, 'error');
|
||||
navigator.mozHasPendingMessage.withArgs('notification').returns(true);
|
||||
|
||||
assert.isFalse(Navigation.hasPendingInit());
|
||||
Navigation.init().catch(() => {}).then(() => {
|
||||
assert.isTrue(Navigation.hasPendingInit());
|
||||
sinon.assert.notCalled(fakeWindow.InboxView.beforeEnter);
|
||||
sinon.assert.notCalled(fakeWindow.InboxView.afterEnter);
|
||||
assert.isFalse(Navigation.isCurrentPanel('thread-list'));
|
||||
assert.isFalse(elements.InboxView.classList.contains('panel-active'));
|
||||
assert.equal(elements.InboxView.getAttribute('aria-hidden'), 'true');
|
||||
sinon.assert.notCalled(console.error);
|
||||
|
||||
// Navigate to composer panel
|
||||
return Navigation.toPanel('composer');
|
||||
}).then(() => {
|
||||
assert.isFalse(Navigation.hasPendingInit());
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Initial navigation request should proceed to the completion ' +
|
||||
'without waiting for the document to be completely loaded and ' +
|
||||
'Navigation to be marked as ready',
|
||||
function(done) {
|
||||
Object.defineProperty(document, 'readyState', { value: 'interactive' });
|
||||
|
||||
var NonReadyNavigation = NavigationFactory(fakeWindow);
|
||||
|
||||
// For the split-view case Navigation.init doesn't wait for the setReady
|
||||
// call or document load event.
|
||||
NonReadyNavigation.init().then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('URL manipulation >', function() {
|
||||
setup(function(done) {
|
||||
setFakeLocation({ pathname: '/views/conversation/', hash: '#?id=3' });
|
||||
|
||||
Navigation.init().then(done, done);
|
||||
});
|
||||
|
||||
test('toPanel changes the location', function(done) {
|
||||
Navigation.toPanel('composer', { id: 3 }).then(() => {
|
||||
sinon.assert.calledWith(
|
||||
fakeWindow.location.assign,
|
||||
'#/composer?id=3'
|
||||
);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
fakeWindow.location.assign = sinon.spy(resolve);
|
||||
Navigation.toPanel('thread-list');
|
||||
});
|
||||
}).then(() => {
|
||||
sinon.assert.calledWith(
|
||||
fakeWindow.location.assign, '/views/inbox/index.html#'
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
209
gaia/apps/sms/views/shared/test/unit/notify_test.js
Executable file
209
gaia/apps/sms/views/shared/test/unit/notify_test.js
Executable file
|
|
@ -0,0 +1,209 @@
|
|||
/*global MockNavigatorSettings, MockAudio, MockVibrate, SettingsURL, Notify,
|
||||
MocksHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_settings_url.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
|
||||
require('/shared/test/unit/mocks/mock_audio.js');
|
||||
require('/views/shared/test/unit/mock_navigator_vibrate.js');
|
||||
require('/views/shared/js/notify.js');
|
||||
|
||||
var mocksHelperNotifications = new MocksHelper([
|
||||
'Audio',
|
||||
'SettingsURL'
|
||||
]).init();
|
||||
|
||||
suite('check the ringtone and vibrate function', function() {
|
||||
var realMozSettings;
|
||||
var isDocumentHidden = true;
|
||||
|
||||
mocksHelperNotifications.attachTestHelpers();
|
||||
|
||||
setup(function() {
|
||||
realMozSettings = navigator.mozSettings;
|
||||
navigator.mozSettings = MockNavigatorSettings;
|
||||
|
||||
// Spy on the mocks
|
||||
this.sinon.spy(Audio.prototype, 'play');
|
||||
this.sinon.stub(navigator, 'vibrate', MockVibrate);
|
||||
this.sinon.spy(window, 'Audio');
|
||||
this.sinon.useFakeTimers();
|
||||
MockNavigatorSettings.mSyncRepliesOnly = true;
|
||||
|
||||
this.sinon.stub(SettingsURL.prototype, 'get', function() {
|
||||
return 'ringtone';
|
||||
});
|
||||
|
||||
this.sinon.stub(SettingsURL.prototype, 'set', function(value) {
|
||||
return value;
|
||||
});
|
||||
|
||||
Object.defineProperty(document, 'hidden', {
|
||||
configurable: true,
|
||||
get: () => isDocumentHidden
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
navigator.mozSettings = realMozSettings;
|
||||
delete document.hidden;
|
||||
});
|
||||
|
||||
function sendVisibilityEvent() {
|
||||
var visibilityEvent = new CustomEvent(
|
||||
'visibilitychange',
|
||||
{ bubbles: true }
|
||||
);
|
||||
|
||||
window.dispatchEvent(visibilityEvent);
|
||||
}
|
||||
|
||||
function assertAudio() {
|
||||
sinon.assert.called(Audio);
|
||||
sinon.assert.called(Audio.prototype.play);
|
||||
|
||||
assert.equal(MockAudio.instances[0].src, 'ringtone');
|
||||
assert.equal(
|
||||
MockAudio.instances[0].mozAudioChannelType, 'notification'
|
||||
);
|
||||
}
|
||||
|
||||
function assertVibrate() {
|
||||
sinon.assert.calledWith(
|
||||
navigator.vibrate,
|
||||
[200, 200, 200, 200]
|
||||
);
|
||||
}
|
||||
|
||||
suite('mozSettings is unavailable', function() {
|
||||
test('We do nothing but we do not stop', function(done) {
|
||||
delete navigator.mozSettings;
|
||||
|
||||
Promise.all([
|
||||
Notify.ringtone(),
|
||||
Notify.vibrate()
|
||||
]).then(() => {
|
||||
sinon.assert.notCalled(Audio.prototype.play);
|
||||
sinon.assert.notCalled(navigator.vibrate);
|
||||
sinon.assert.notCalled(Audio);
|
||||
}).then(done, done);
|
||||
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
});
|
||||
|
||||
suite('volume is 1 and vibration is enabled', function() {
|
||||
test('play ringtone and vibrate', function(done) {
|
||||
var settings = {
|
||||
'audio.volume.notification': 1,
|
||||
'notification.ringtone': 'ringtone',
|
||||
'vibration.enabled': true
|
||||
};
|
||||
navigator.mozSettings.createLock().set(settings);
|
||||
|
||||
Promise.all([
|
||||
Notify.ringtone(),
|
||||
Notify.vibrate()
|
||||
]).then(() => {
|
||||
|
||||
// As document is not shown in the test, we launch the
|
||||
// event of visibility
|
||||
sendVisibilityEvent();
|
||||
assertAudio();
|
||||
assertVibrate();
|
||||
}).then(done, done);
|
||||
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
});
|
||||
|
||||
suite('volume is 1 and vibration is disabled', function() {
|
||||
test('play ringtone and do not vibrate', function(done) {
|
||||
var settings = {
|
||||
'audio.volume.notification': 1,
|
||||
'notification.ringtone': 'ringtone',
|
||||
'vibration.enabled': false
|
||||
};
|
||||
navigator.mozSettings.createLock().set(settings);
|
||||
|
||||
Promise.all([
|
||||
Notify.ringtone(),
|
||||
Notify.vibrate()
|
||||
]).then(() => {
|
||||
sinon.assert.notCalled(navigator.vibrate);
|
||||
assertAudio();
|
||||
}).then(done, done);
|
||||
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
});
|
||||
|
||||
suite('volume is 0 and vibration is enabled', function() {
|
||||
test('do not play ringtone and vibrate', function(done) {
|
||||
var settings = {
|
||||
'audio.volume.notification': 0,
|
||||
'notification.ringtone': 'ringtone',
|
||||
'vibration.enabled': true
|
||||
};
|
||||
navigator.mozSettings.createLock().set(settings);
|
||||
|
||||
Promise.all([
|
||||
Notify.ringtone(),
|
||||
Notify.vibrate()
|
||||
]).then(() => {
|
||||
// As document is not shown in the test, we launch the
|
||||
// event of visibility
|
||||
sendVisibilityEvent();
|
||||
|
||||
sinon.assert.notCalled(Audio.prototype.play);
|
||||
sinon.assert.notCalled(Audio);
|
||||
|
||||
assertVibrate();
|
||||
}).then(done, done);
|
||||
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
|
||||
test('vibrate asap if the app is displayed', function(done) {
|
||||
var settings = {
|
||||
'audio.volume.notification': 0,
|
||||
'notification.ringtone': 'ringtone',
|
||||
'vibration.enabled': true
|
||||
};
|
||||
navigator.mozSettings.createLock().set(settings);
|
||||
isDocumentHidden = false;
|
||||
|
||||
Promise.all([
|
||||
Notify.ringtone(),
|
||||
Notify.vibrate()
|
||||
]).then(() => {
|
||||
assertVibrate();
|
||||
}).then(done, done);
|
||||
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
});
|
||||
|
||||
suite('volume is 0 and vibration is disabled', function() {
|
||||
test('play ringtone and do not vibrate', function(done) {
|
||||
var settings = {
|
||||
'audio.volume.notification': 0,
|
||||
'notification.ringtone': 'ringtone',
|
||||
'vibration.enabled': false
|
||||
};
|
||||
navigator.mozSettings.createLock().set(settings);
|
||||
|
||||
Promise.all([
|
||||
Notify.ringtone(),
|
||||
Notify.vibrate()
|
||||
]).then(() => {
|
||||
sinon.assert.notCalled(Audio.prototype.play);
|
||||
sinon.assert.notCalled(navigator.vibrate);
|
||||
sinon.assert.notCalled(Audio);
|
||||
}).then(done, done);
|
||||
|
||||
MockNavigatorSettings.mReplyToRequests();
|
||||
});
|
||||
});
|
||||
});
|
||||
200
gaia/apps/sms/views/shared/test/unit/selection_handler_test.js
Executable file
200
gaia/apps/sms/views/shared/test/unit/selection_handler_test.js
Executable file
|
|
@ -0,0 +1,200 @@
|
|||
/*global SelectionHandler
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/js/selection_handler.js');
|
||||
|
||||
suite('SelectionHandler', function() {
|
||||
const LENGTH = 10;
|
||||
var container, checkUncheckAllButton, selectionHandler, options;
|
||||
|
||||
setup(function() {
|
||||
container = document.createElement('article');
|
||||
checkUncheckAllButton = document.createElement('button');
|
||||
|
||||
var innerHTML = '';
|
||||
var idSet = new Set();
|
||||
for (var i = 0; i < LENGTH; i++) {
|
||||
innerHTML += '<label>' +
|
||||
'<input type="checkbox" value="' + i + '">' +
|
||||
'<span></span></label>';
|
||||
idSet.add(i);
|
||||
}
|
||||
container.innerHTML = innerHTML;
|
||||
|
||||
options = {
|
||||
container: container,
|
||||
checkUncheckAllButton: checkUncheckAllButton,
|
||||
updateSelectionStatus: sinon.stub(),
|
||||
getIdIterator: sinon.stub().returns(idSet.values()),
|
||||
isInEditMode: sinon.stub()
|
||||
};
|
||||
});
|
||||
|
||||
suite('create SelectionHandler instance', function() {
|
||||
test('throws if options is not valid', function() {
|
||||
assert.throws(() => new SelectionHandler());
|
||||
assert.throws(() => new SelectionHandler({}));
|
||||
assert.throws(() => new SelectionHandler({
|
||||
// One element missed
|
||||
container: container,
|
||||
// Methods
|
||||
updateSelectionStatus: sinon.stub(),
|
||||
getIdIterator: sinon.stub(),
|
||||
isInEditMode: sinon.stub()
|
||||
}));
|
||||
});
|
||||
|
||||
test('correctly set selected Set', function() {
|
||||
var options = {
|
||||
container: container,
|
||||
checkUncheckAllButton: checkUncheckAllButton,
|
||||
updateSelectionStatus: sinon.stub(),
|
||||
getIdIterator: sinon.stub(),
|
||||
isInEditMode: sinon.stub()
|
||||
};
|
||||
selectionHandler = new SelectionHandler(options);
|
||||
|
||||
assert.isTrue(selectionHandler.selected instanceof Set);
|
||||
|
||||
for (var key in options) {
|
||||
assert.equal(options[key], selectionHandler[key]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suite('onSelected', function() {
|
||||
setup(function() {
|
||||
options.isInEditMode.returns(true);
|
||||
selectionHandler = new SelectionHandler(options);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
container.innerHTML = '';
|
||||
});
|
||||
|
||||
test('Early exist if not in edit mode', function() {
|
||||
selectionHandler.isInEditMode.returns(false);
|
||||
container.querySelectorAll('input')[0].click();
|
||||
|
||||
sinon.assert.notCalled(selectionHandler.updateSelectionStatus);
|
||||
assert.equal(selectionHandler.selectedCount, 0);
|
||||
});
|
||||
|
||||
test('input selected/unselected', function() {
|
||||
var target = container.querySelectorAll('input')[0];
|
||||
var event = new MouseEvent('click', {
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
|
||||
target.dispatchEvent(event);
|
||||
|
||||
sinon.assert.called(selectionHandler.updateSelectionStatus);
|
||||
assert.equal(selectionHandler.selectedCount, 1);
|
||||
assert.equal(selectionHandler.selectedList[0], target.value);
|
||||
|
||||
target.dispatchEvent(event);
|
||||
|
||||
sinon.assert.called(selectionHandler.updateSelectionStatus);
|
||||
assert.equal(selectionHandler.selectedCount, 0);
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateCheckboxes', function() {
|
||||
setup(function() {
|
||||
selectionHandler = new SelectionHandler(options);
|
||||
});
|
||||
|
||||
test('checkbox UI should sync up with data', function() {
|
||||
var allInputs = container.querySelectorAll('input');
|
||||
var selectedIds = [1,2];
|
||||
|
||||
selectedIds.forEach((id) => {
|
||||
selectionHandler.select(id);
|
||||
});
|
||||
|
||||
// No checkbox selected on UI
|
||||
Array.prototype.forEach.call(allInputs, (input) => {
|
||||
assert.isFalse(input.checked);
|
||||
});
|
||||
|
||||
selectionHandler.updateCheckboxes();
|
||||
|
||||
// UI should sync up with selected data
|
||||
Array.prototype.forEach.call(allInputs, (input, index) => {
|
||||
assert.equal(
|
||||
input.checked,
|
||||
selectedIds.indexOf(index) > -1
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('toggleCheckedAll', function() {
|
||||
setup(function() {
|
||||
selectionHandler = new SelectionHandler(options);
|
||||
|
||||
this.sinon.stub(selectionHandler, 'updateCheckboxes');
|
||||
});
|
||||
|
||||
test('Should select all when not all data selected first', function() {
|
||||
selectionHandler.select(1);
|
||||
selectionHandler.toggleCheckedAll();
|
||||
|
||||
assert.equal(selectionHandler.selectedCount, LENGTH);
|
||||
});
|
||||
|
||||
test('Should unselect all when all selected first', function() {
|
||||
for (var id of selectionHandler.inputIdSet) {
|
||||
selectionHandler.select(id);
|
||||
}
|
||||
|
||||
assert.equal(selectionHandler.selectedCount, LENGTH);
|
||||
|
||||
selectionHandler.toggleCheckedAll();
|
||||
assert.equal(selectionHandler.selectedCount, 0);
|
||||
});
|
||||
});
|
||||
|
||||
suite('cleanForm', function() {
|
||||
setup(function() {
|
||||
selectionHandler = new SelectionHandler(options);
|
||||
|
||||
this.sinon.stub(selectionHandler, 'updateCheckboxes');
|
||||
});
|
||||
|
||||
test('selected data cleared', function() {
|
||||
// 1 data added
|
||||
selectionHandler.select(1);
|
||||
assert.equal(selectionHandler.selectedCount, 1);
|
||||
|
||||
// Data is cleared after cleanForm
|
||||
selectionHandler.cleanForm();
|
||||
assert.equal(selectionHandler.selectedCount, 0);
|
||||
sinon.assert.called(selectionHandler.updateCheckboxes);
|
||||
});
|
||||
});
|
||||
|
||||
suite('allSelected', function() {
|
||||
setup(function() {
|
||||
selectionHandler = new SelectionHandler(options);
|
||||
});
|
||||
|
||||
test('return false if none or some selected', function() {
|
||||
assert.isFalse(selectionHandler.allSelected());
|
||||
|
||||
selectionHandler.select(0);
|
||||
assert.isFalse(selectionHandler.allSelected());
|
||||
});
|
||||
|
||||
test('return true if all selected', function() {
|
||||
for (var id of selectionHandler.inputIdSet) {
|
||||
selectionHandler.select(id);
|
||||
}
|
||||
|
||||
assert.isTrue(selectionHandler.allSelected());
|
||||
});
|
||||
});
|
||||
});
|
||||
351
gaia/apps/sms/views/shared/test/unit/settings_test.js
Executable file
351
gaia/apps/sms/views/shared/test/unit/settings_test.js
Executable file
|
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
Settings Tests
|
||||
*/
|
||||
|
||||
/*global Settings,
|
||||
MockNavigatorSettings,
|
||||
MockL10n
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l20n.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
|
||||
require('/views/shared/js/settings.js');
|
||||
|
||||
suite('Settings >', function() {
|
||||
var nativeSettings;
|
||||
|
||||
setup(function() {
|
||||
nativeSettings = navigator.mozSettings;
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
MockNavigatorSettings.mTeardown();
|
||||
navigator.mozSettings = nativeSettings;
|
||||
Settings.mmsSizeLimitation = 295 * 1024;
|
||||
Settings.maxConcatenatedMessages = 10;
|
||||
});
|
||||
|
||||
suite('Without mozSettings', function() {
|
||||
setup(function() {
|
||||
navigator.mozSettings = null;
|
||||
Settings.mmsSizeLimitation = 'whatever is default';
|
||||
Settings.maxConcatenatedMessages = 'whatever is default';
|
||||
Settings.mmsServiceId = 'no service ID';
|
||||
Settings.smsServiceId = 'no service ID';
|
||||
|
||||
Settings.init();
|
||||
});
|
||||
|
||||
test('Query Max concatenated Messages without settings', function() {
|
||||
assert.equal(Settings.maxConcatenatedMessages, 'whatever is default');
|
||||
});
|
||||
|
||||
test('Query size limitation without settings', function() {
|
||||
assert.equal(Settings.mmsSizeLimitation, 'whatever is default');
|
||||
});
|
||||
|
||||
test('Query mmsServiceId without settings', function() {
|
||||
assert.equal(Settings.mmsServiceId, 'no service ID');
|
||||
});
|
||||
|
||||
test('Query smsServiceId without settings', function() {
|
||||
assert.equal(Settings.smsServiceId, 'no service ID');
|
||||
});
|
||||
|
||||
test('Reports no dual SIM', function() {
|
||||
assert.isFalse(Settings.isDualSimDevice());
|
||||
assert.isFalse(Settings.hasSeveralSim());
|
||||
});
|
||||
|
||||
test('getServiceIdByIccId returns null', function() {
|
||||
assert.isNull(Settings.getServiceIdByIccId('anything'));
|
||||
});
|
||||
|
||||
test('setReadAheadThreadRetrieval does nothing', function() {
|
||||
assert.doesNotThrow(() => {
|
||||
Settings.setReadAheadThreadRetrieval(9);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('With mozSettings', function() {
|
||||
var realMozL10n, realSettings;
|
||||
|
||||
function findSettingsReq(key) {
|
||||
var locks = navigator.mozSettings.createLock.returnValues;
|
||||
|
||||
var foundLock = locks.find(function(lock) {
|
||||
return lock.get.calledWith(key);
|
||||
});
|
||||
|
||||
return foundLock ? foundLock.get.firstCall.returnValue : null;
|
||||
}
|
||||
|
||||
function triggerSettingsReqSuccess(setting, value) {
|
||||
var req = findSettingsReq(setting);
|
||||
req.result = {};
|
||||
req.result[setting] = value;
|
||||
req.onsuccess();
|
||||
}
|
||||
|
||||
function assertSettingIsRetrieved(prop, setting, value, expected) {
|
||||
triggerSettingsReqSuccess(setting, value);
|
||||
|
||||
if (arguments.length <= 3) {
|
||||
expected = value;
|
||||
}
|
||||
|
||||
assert.equal(
|
||||
Settings[prop], expected,
|
||||
'The setting ' + setting + ' is equal to ' + expected
|
||||
);
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
realMozL10n = document.l10n;
|
||||
document.l10n = MockL10n;
|
||||
|
||||
realSettings = navigator.mozSettings;
|
||||
navigator.mozSettings = MockNavigatorSettings;
|
||||
|
||||
Settings.mmsSizeLimitation = 'whatever is default';
|
||||
Settings.maxConcatenatedMessages = 'whatever is default';
|
||||
Settings.mmsServiceId = 'no service ID';
|
||||
|
||||
this.sinon.stub(navigator.mozSettings, 'createLock', function() {
|
||||
var api = {
|
||||
get: function() {
|
||||
return {};
|
||||
},
|
||||
set: function() {}
|
||||
};
|
||||
sinon.spy(api, 'get');
|
||||
sinon.spy(api, 'set');
|
||||
return api;
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
document.l10n = realMozL10n;
|
||||
realMozL10n = null;
|
||||
|
||||
navigator.mozSettings = realSettings;
|
||||
realSettings = null;
|
||||
});
|
||||
|
||||
test('Query size limitation with settings exist ' +
|
||||
'(Size Limitation = 500KB and maxConcat = 10)', function() {
|
||||
Settings.init();
|
||||
assert.equal(Settings.mmsSizeLimitation, 'whatever is default');
|
||||
assert.equal(Settings.maxConcatenatedMessages, 'whatever is default');
|
||||
|
||||
// only made two calls (maxConcatenatedMessages/mmsSizeLimitation)
|
||||
// to get settings(non-DSDS case)
|
||||
sinon.assert.calledTwice(navigator.mozSettings.createLock);
|
||||
|
||||
var setting = 512 * 1024;
|
||||
var expected = setting - 5 * 1024;
|
||||
|
||||
assertSettingIsRetrieved(
|
||||
'mmsSizeLimitation',
|
||||
'dom.mms.operatorSizeLimitation',
|
||||
setting,
|
||||
expected
|
||||
);
|
||||
assertSettingIsRetrieved(
|
||||
'maxConcatenatedMessages',
|
||||
'operatorResource.sms.maxConcat',
|
||||
10
|
||||
);
|
||||
|
||||
assert.isFalse(Settings.hasSeveralSim());
|
||||
});
|
||||
|
||||
suite('in a dual SIM device with 2 SIMs,', function() {
|
||||
setup(function() {
|
||||
// TODO use the mocks from shared and update them according to latest
|
||||
// spec
|
||||
navigator.mozMobileConnections = [
|
||||
{ iccId: 'SIM 1' },
|
||||
{ iccId: 'SIM 2' }
|
||||
];
|
||||
Settings.init();
|
||||
});
|
||||
|
||||
test('init is correctly executed', function() {
|
||||
assert.equal(Settings.mmsServiceId, 'no service ID');
|
||||
|
||||
// Four calls for
|
||||
// maxConcatenatedMessages/mmsSizeLimitation/mmsServiceId/smsServiceId
|
||||
sinon.assert.callCount(navigator.mozSettings.createLock, 4);
|
||||
});
|
||||
|
||||
test('Dual SIM state is correctly reported', function() {
|
||||
assert.isTrue(Settings.hasSeveralSim());
|
||||
assert.isTrue(Settings.isDualSimDevice());
|
||||
});
|
||||
|
||||
test('the settings are correctly retrieved', function() {
|
||||
for (var prop in Settings.SERVICE_ID_KEYS) {
|
||||
var setting = Settings.SERVICE_ID_KEYS[prop];
|
||||
assertSettingIsRetrieved(prop, setting, 0);
|
||||
}
|
||||
});
|
||||
|
||||
test('getServiceIdByIccId returns the correct id', function() {
|
||||
assert.equal(Settings.getServiceIdByIccId('SIM 1'), 0);
|
||||
assert.equal(Settings.getServiceIdByIccId('SIM 2'), 1);
|
||||
assert.isNull(Settings.getServiceIdByIccId('SIM 3'));
|
||||
});
|
||||
});
|
||||
|
||||
test('in a single SIM device', function() {
|
||||
navigator.mozMobileConnections = [{ iccId: 'SIM 1' }];
|
||||
Settings.init();
|
||||
|
||||
// Two calls for
|
||||
// maxConcatenatedMessages/mmsSizeLimitation
|
||||
sinon.assert.calledTwice(navigator.mozSettings.createLock);
|
||||
for (var prop in Settings.SERVICE_ID_KEYS) {
|
||||
var setting = Settings.SERVICE_ID_KEYS[prop];
|
||||
assert.isNull(findSettingsReq(setting));
|
||||
assert.equal(Settings[prop], 'no service ID');
|
||||
}
|
||||
assert.isFalse(Settings.hasSeveralSim());
|
||||
assert.isFalse(Settings.isDualSimDevice());
|
||||
assert.isNull(Settings.getServiceIdByIccId('SIM 1'));
|
||||
});
|
||||
|
||||
test('in a dual SIM device with only 1 SIM', function() {
|
||||
navigator.mozMobileConnections = [{ iccId: 'SIM 1' }, { iccId: null }];
|
||||
Settings.init();
|
||||
|
||||
// Four calls for
|
||||
// maxConcatenatedMessages/mmsSizeLimitation/mmsServiceId/smsServiceId
|
||||
sinon.assert.callCount(navigator.mozSettings.createLock, 4);
|
||||
|
||||
for (var prop in Settings.SERVICE_ID_KEYS) {
|
||||
var setting = Settings.SERVICE_ID_KEYS[prop];
|
||||
assert.ok(findSettingsReq(setting));
|
||||
}
|
||||
assert.isFalse(Settings.hasSeveralSim());
|
||||
assert.isTrue(Settings.isDualSimDevice());
|
||||
assert.equal(Settings.getServiceIdByIccId('SIM 1'), 0);
|
||||
});
|
||||
|
||||
test('in a triple SIM device with 2 SIMs', function() {
|
||||
navigator.mozMobileConnections = [
|
||||
{ iccId: 'SIM 1' },
|
||||
{ iccId: 'SIM 2' },
|
||||
{ iccId: null }
|
||||
];
|
||||
Settings.init();
|
||||
|
||||
assert.isTrue(Settings.hasSeveralSim());
|
||||
assert.isTrue(Settings.isDualSimDevice());
|
||||
});
|
||||
|
||||
test('mmsServiceId observer and update', function() {
|
||||
navigator.mozMobileConnections = [
|
||||
{ iccId: 'SIM 1' },
|
||||
{ iccId: 'SIM 2' }
|
||||
];
|
||||
Settings.init();
|
||||
|
||||
for (var prop in Settings.SERVICE_ID_KEYS) {
|
||||
var setting = Settings.SERVICE_ID_KEYS[prop];
|
||||
triggerSettingsReqSuccess(setting, 0);
|
||||
MockNavigatorSettings.mTriggerObservers(setting, {settingValue: 1});
|
||||
assert.equal(Settings[prop], 1);
|
||||
}
|
||||
});
|
||||
|
||||
suite('switchSimHandler for async callback when ready', function() {
|
||||
var conn;
|
||||
var listenerSpy;
|
||||
|
||||
// The real navigator.mozMobileConnections is not a real array
|
||||
var mockMozMobileConnections = {
|
||||
0: {
|
||||
iccId: 'SIM 1',
|
||||
addEventListener: function() {},
|
||||
removeEventListener: function() {},
|
||||
data: {
|
||||
state: 'searching'
|
||||
}
|
||||
},
|
||||
1: {
|
||||
iccId: 'SIM 2',
|
||||
addEventListener: function() {},
|
||||
removeEventListener: function() {},
|
||||
data: {
|
||||
state: 'searching'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setup(function() {
|
||||
if (!('mozMobileConnections' in navigator)) {
|
||||
navigator.mozMobileConnections = null;
|
||||
}
|
||||
|
||||
// let's use the mock's implementation
|
||||
navigator.mozSettings.createLock.restore();
|
||||
|
||||
this.sinon.stub(window.navigator, 'mozMobileConnections',
|
||||
mockMozMobileConnections);
|
||||
|
||||
conn = window.navigator.mozMobileConnections[1];
|
||||
listenerSpy = this.sinon.spy(conn, 'addEventListener');
|
||||
this.sinon.stub(conn, 'removeEventListener');
|
||||
this.sinon.stub(console, 'error');
|
||||
Settings.init();
|
||||
|
||||
});
|
||||
|
||||
test('Should return resolve directly if state is already registered',
|
||||
function(done) {
|
||||
conn.data.state = 'registered';
|
||||
Settings.switchMmsSimHandler(1).then(function onsuccess(){
|
||||
sinon.assert.notCalled(listenerSpy);
|
||||
}).then(done,done);
|
||||
});
|
||||
|
||||
test('Should not be triggered if connection does not exist',
|
||||
function(done) {
|
||||
Settings.switchMmsSimHandler('invalid').catch(function onerror(err){
|
||||
sinon.assert.notCalled(conn.removeEventListener);
|
||||
assert.equal(err, 'Invalid connection');
|
||||
}).then(done,done);
|
||||
});
|
||||
|
||||
test('callback when data connection state changes', function(done) {
|
||||
conn.data.state = 'searching';
|
||||
Settings.switchMmsSimHandler(1).then(function onsuccess(){
|
||||
assert.equal(
|
||||
MockNavigatorSettings.mSettings['ril.mms.defaultServiceId'], 1
|
||||
);
|
||||
assert.equal(
|
||||
MockNavigatorSettings.mSettings['ril.data.defaultServiceId'], 1
|
||||
);
|
||||
sinon.assert.calledOnce(conn.removeEventListener);
|
||||
}).then(done,done);
|
||||
|
||||
conn.data.state = 'registered';
|
||||
listenerSpy.yield();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
test('setReadAheadThreadRetrieval()', function() {
|
||||
Settings.setReadAheadThreadRetrieval(9);
|
||||
|
||||
sinon.assert.calledWithMatch(
|
||||
navigator.mozSettings.createLock.lastCall.returnValue.set,
|
||||
{ 'ril.sms.maxReadAheadEntries' : 9 }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
130
gaia/apps/sms/views/shared/test/unit/shared_components_test.js
Executable file
130
gaia/apps/sms/views/shared/test/unit/shared_components_test.js
Executable file
|
|
@ -0,0 +1,130 @@
|
|||
/* global SharedComponents, Template */
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/js/shared_components.js');
|
||||
|
||||
suite('SharedComponents >', function() {
|
||||
setup(function() {
|
||||
loadBodyHTML('/index.html');
|
||||
});
|
||||
|
||||
suite('phoneDetails >', function() {
|
||||
test('localized correctly', function() {
|
||||
var phoneDetails = SharedComponents.phoneDetails({
|
||||
number: '1',
|
||||
type: 'Mobile'
|
||||
}).toDocumentFragment();
|
||||
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-type').getAttribute('data-l10n-id'),
|
||||
'Mobile'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-type-separator')
|
||||
.getAttribute('data-l10n-id'),
|
||||
'phone-type-separator'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-carrier-separator')
|
||||
.getAttribute('data-l10n-id'),
|
||||
'phone-carrier-separator'
|
||||
);
|
||||
});
|
||||
|
||||
test('no phone details passed', function() {
|
||||
assert.throws(function() {
|
||||
SharedComponents.phoneDetails();
|
||||
});
|
||||
|
||||
assert.throws(function() {
|
||||
SharedComponents.phoneDetails(null, {});
|
||||
});
|
||||
});
|
||||
|
||||
test('no phone type, no phone carrier', function() {
|
||||
var phoneDetails = SharedComponents.phoneDetails({
|
||||
number: '1'
|
||||
}).toDocumentFragment();
|
||||
|
||||
assert.isNull(phoneDetails.querySelector('.has-phone-type'));
|
||||
assert.isNull(phoneDetails.querySelector('.has-phone-carrier'));
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-number').textContent, '1'
|
||||
);
|
||||
});
|
||||
|
||||
test('no phone carrier', function() {
|
||||
var phoneDetails = SharedComponents.phoneDetails({
|
||||
number: '1',
|
||||
type: 'Mobile'
|
||||
}).toDocumentFragment();
|
||||
|
||||
assert.isNotNull(phoneDetails.querySelector('.has-phone-type'));
|
||||
assert.isNull(phoneDetails.querySelector('.has-phone-carrier'));
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-number').textContent, '1'
|
||||
);
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-type').textContent, 'Mobile'
|
||||
);
|
||||
});
|
||||
|
||||
test('no phone type, but with carrier', function() {
|
||||
var phoneDetails = SharedComponents.phoneDetails({
|
||||
number: '1',
|
||||
carrier: 'MTS'
|
||||
}).toDocumentFragment();
|
||||
|
||||
assert.isNull(phoneDetails.querySelector('.has-phone-type'));
|
||||
assert.isNotNull(phoneDetails.querySelector('.has-phone-carrier'));
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-number').textContent, '1'
|
||||
);
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-carrier').textContent, 'MTS'
|
||||
);
|
||||
});
|
||||
|
||||
test('with phone type and carrier', function() {
|
||||
var phoneDetails = SharedComponents.phoneDetails({
|
||||
number: '1',
|
||||
type: 'Mobile',
|
||||
carrier: 'MTS'
|
||||
}).toDocumentFragment();
|
||||
|
||||
assert.isNotNull(phoneDetails.querySelector('.has-phone-type'));
|
||||
assert.isNotNull(phoneDetails.querySelector('.has-phone-carrier'));
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-number').textContent, '1'
|
||||
);
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-type').textContent, 'Mobile'
|
||||
);
|
||||
assert.equal(
|
||||
phoneDetails.querySelector('.phone-carrier').textContent, 'MTS'
|
||||
);
|
||||
});
|
||||
|
||||
test('with interpolation options', function() {
|
||||
this.sinon.spy(Template.prototype, 'prepare');
|
||||
|
||||
var interpolationOptions = {
|
||||
safe: ['number']
|
||||
};
|
||||
|
||||
SharedComponents.phoneDetails({
|
||||
number: '1',
|
||||
type: 'Mobile',
|
||||
carrier: 'MTS'
|
||||
}, interpolationOptions);
|
||||
|
||||
sinon.assert.calledWithMatch(
|
||||
Template.prototype.prepare,
|
||||
sinon.match.any,
|
||||
interpolationOptions
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
287
gaia/apps/sms/views/shared/test/unit/silent_sms_test.js
Executable file
287
gaia/apps/sms/views/shared/test/unit/silent_sms_test.js
Executable file
|
|
@ -0,0 +1,287 @@
|
|||
/* global MockasyncStorage, SilentSms, MockMozAlarms, MocksHelper,
|
||||
MockNavigatormozSetMessageHandler
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/test/unit/mock_moz_alarm.js');
|
||||
require('/views/shared/test/unit/mock_async_storage.js');
|
||||
require('/shared/test/unit/mocks/mock_navigator_moz_set_message_handler.js');
|
||||
require('/views/shared/js/task_runner.js');
|
||||
require('/views/shared/js/silent_sms.js');
|
||||
|
||||
var mocksHelperForActivityHandler = new MocksHelper(['asyncStorage']).init();
|
||||
|
||||
suite('Silent SMS > ', function() {
|
||||
mocksHelperForActivityHandler.attachTestHelpers();
|
||||
|
||||
var realMozSetMessageHandler,
|
||||
realMozAlarms,
|
||||
mozAlarmsId = 0;
|
||||
var FakePort = function() {};
|
||||
FakePort.prototype.postMessageToSms = function(e) {
|
||||
this.onmessage.call(this, e);
|
||||
};
|
||||
FakePort.prototype.postMessage = function(e) {};
|
||||
|
||||
function MockAlarmRequest(willFail) {
|
||||
this._willFail = willFail;
|
||||
}
|
||||
|
||||
Object.defineProperty(MockAlarmRequest.prototype, 'onsuccess', {
|
||||
set: function (callback) {
|
||||
if (!this._willFail) {
|
||||
this.result = mozAlarmsId++;
|
||||
(typeof callback === 'function') &&
|
||||
callback.call(this, { target: this });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(MockAlarmRequest.prototype, 'onerror', {
|
||||
set: function (callback) {
|
||||
if (this._willFail) {
|
||||
this.error = { name: 'error' };
|
||||
(typeof callback === 'function') &&
|
||||
callback.call(this, { target: this });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function newSuccessfulRequest() {
|
||||
return new MockAlarmRequest(false);
|
||||
}
|
||||
|
||||
function newFailingRequest() {
|
||||
return new MockAlarmRequest(true);
|
||||
}
|
||||
|
||||
suiteSetup(function() {
|
||||
realMozSetMessageHandler = navigator.mozSetMessageHandler;
|
||||
navigator.mozSetMessageHandler = MockNavigatormozSetMessageHandler;
|
||||
|
||||
MockNavigatormozSetMessageHandler.mSetup();
|
||||
|
||||
realMozAlarms = window.navigator.mozAlarms;
|
||||
window.navigator.mozAlarms = MockMozAlarms;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozSetMessageHandler = realMozSetMessageHandler;
|
||||
window.navigator.mozAlarms = realMozAlarms;
|
||||
});
|
||||
|
||||
suite('connection from usage', function() {
|
||||
var request,
|
||||
message;
|
||||
var fakePort = new FakePort();
|
||||
|
||||
setup(function() {
|
||||
SilentSms.init();
|
||||
request = {
|
||||
keyword: 'costcontrolSmsQuery',
|
||||
port: fakePort
|
||||
};
|
||||
MockNavigatormozSetMessageHandler.mTrigger('connection', request);
|
||||
|
||||
message = {
|
||||
action: 'enable',
|
||||
smsNumbers: [1234],
|
||||
type: 'balance'
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
MockNavigatormozSetMessageHandler.mTeardown();
|
||||
MockasyncStorage.mTeardown();
|
||||
message = null;
|
||||
});
|
||||
|
||||
test('silentMode is enabled correctly by IAC', function(done) {
|
||||
var request = newSuccessfulRequest();
|
||||
this.sinon.stub(navigator.mozAlarms, 'add').returns(request);
|
||||
|
||||
var smsToSilent = [1234, 111, 234];
|
||||
message.smsNumbers = smsToSilent;
|
||||
this.sinon.stub(
|
||||
FakePort.prototype, 'postMessage',
|
||||
function assertionFunction(e) {
|
||||
done(function() {
|
||||
assert.equal(e, 'silentSMSEnabled');
|
||||
var usageSilentSms = MockasyncStorage.keys.usageSilentSms;
|
||||
var alarmId = usageSilentSms[message.type].alarmId;
|
||||
|
||||
assert.isDefined(alarmId);
|
||||
assert.deepEqual(usageSilentSms[message.type].smsNumbers,
|
||||
smsToSilent);
|
||||
var smsIndex = usageSilentSms.smsIndex;
|
||||
smsToSilent.forEach(function (silentSmsNumber) {
|
||||
assert.isTrue(smsIndex.indexOf(silentSmsNumber) !== -1);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
fakePort.postMessageToSms({
|
||||
data: message
|
||||
});
|
||||
});
|
||||
|
||||
test('silentMode is disabled correctly by IAC', function(done) {
|
||||
var request = newSuccessfulRequest();
|
||||
this.sinon.stub(navigator.mozAlarms, 'add').returns(request);
|
||||
|
||||
var alarmId, usageSilentSms;
|
||||
this.sinon.stub(
|
||||
FakePort.prototype, 'postMessage',
|
||||
function assertionFunction(actionMsg) {
|
||||
if (actionMsg === 'silentSMSEnabled') {
|
||||
try {
|
||||
usageSilentSms = MockasyncStorage.keys.usageSilentSms;
|
||||
// The Timeout alarm is actived and usageSilentSms has been
|
||||
// updated
|
||||
alarmId = usageSilentSms[message.type].alarmId;
|
||||
assert.ok(alarmId !== undefined);
|
||||
|
||||
// Generating call for disable the silentMode for balance
|
||||
message.action = 'disable';
|
||||
|
||||
fakePort.postMessageToSms({
|
||||
data: message
|
||||
});
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (actionMsg === 'silentSMSDisabled') {
|
||||
done(function() {
|
||||
usageSilentSms = MockasyncStorage.keys.usageSilentSms;
|
||||
// Check the usageSilentSms object was update
|
||||
assert.isNull(usageSilentSms[message.type]);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
fakePort.postMessageToSms({
|
||||
data: message
|
||||
});
|
||||
});
|
||||
|
||||
test('silentMode is disabled correctly by an alarm', function(done) {
|
||||
var mAlarmData = {data: {
|
||||
alarmType: 'silentSmsTimeout',
|
||||
type: 'balance'
|
||||
}};
|
||||
|
||||
MockasyncStorage.keys.usageSilentSms = {
|
||||
balance: {smsNumbers:[1234,111,234], alarmId: 1},
|
||||
smsIndex: [1234,111,234]
|
||||
};
|
||||
|
||||
// Mocking asyncStorage because it's the last method called after an
|
||||
// alarm is fired.
|
||||
this.sinon.stub(MockasyncStorage, 'setItem', function(usageSilentSMS) {
|
||||
done(function() {
|
||||
usageSilentSMS = MockasyncStorage.keys.usageSilentSms;
|
||||
// Check the usageSilentSms object was update
|
||||
assert.isNull(usageSilentSMS.balance);
|
||||
});
|
||||
});
|
||||
|
||||
// Firing the alarm to disable the balance silentMode
|
||||
MockNavigatormozSetMessageHandler.mTrigger('alarm', mAlarmData);
|
||||
|
||||
});
|
||||
|
||||
test('Received two enabled request for the same type before the timeout' +
|
||||
' alarm is fired', function(done) {
|
||||
var request = newSuccessfulRequest();
|
||||
this.sinon.stub(navigator.mozAlarms, 'add').returns(request);
|
||||
|
||||
var isFirstTime = true,
|
||||
alarmId;
|
||||
this.sinon.stub(
|
||||
FakePort.prototype, 'postMessage',
|
||||
function assertionFunction(e) {
|
||||
assert.equal(e, 'silentSMSEnabled');
|
||||
var usageSilentSms = MockasyncStorage.keys.usageSilentSms;
|
||||
if (isFirstTime) {
|
||||
try {
|
||||
isFirstTime = false;
|
||||
alarmId = usageSilentSms[message.type].alarmId;
|
||||
assert.ok(alarmId !== undefined);
|
||||
|
||||
// Launched the second call
|
||||
fakePort.postMessageToSms({
|
||||
data: message
|
||||
});
|
||||
} catch(error) {
|
||||
done(error);
|
||||
}
|
||||
} else {
|
||||
done(function() {
|
||||
// The second call removes the first timeout alarm and adds
|
||||
// another one
|
||||
var newAlarmId = usageSilentSms[message.type].alarmId;
|
||||
assert.notEqual(alarmId, newAlarmId);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
fakePort.postMessageToSms({
|
||||
data: message
|
||||
});
|
||||
});
|
||||
|
||||
test('silentMode is enabled correctly by IAC when addAlarm fails',
|
||||
function(done) {
|
||||
var request = newFailingRequest();
|
||||
this.sinon.stub(navigator.mozAlarms, 'add').returns(request);
|
||||
|
||||
var NO_ALARM_ID = -1;
|
||||
this.sinon.stub(
|
||||
FakePort.prototype, 'postMessage',
|
||||
function assertionFunction(e) {
|
||||
done(function(){
|
||||
assert.equal(e, 'silentSMSEnabled');
|
||||
var usageSilentSms = MockasyncStorage.keys.usageSilentSms;
|
||||
var alarmId = usageSilentSms[message.type].alarmId;
|
||||
assert.equal(alarmId, NO_ALARM_ID);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
fakePort.postMessageToSms({
|
||||
data: message
|
||||
});
|
||||
});
|
||||
|
||||
test('Check checkSilentModeFor behaviour', function (done) {
|
||||
|
||||
var fakeUsageSilentSms = {
|
||||
'balance': {
|
||||
'smsNumbers': [1234, 111, 234],
|
||||
'alarmId': 0,
|
||||
'timeAlarm': new Date()
|
||||
},
|
||||
'topup': {
|
||||
'smsNumbers': [888, 222],
|
||||
'alarmId': 0,
|
||||
'timeAlarm': new Date()
|
||||
},
|
||||
'smsIndex':[1234, 111, 234, 888, 222]
|
||||
};
|
||||
MockasyncStorage.keys.usageSilentSms = fakeUsageSilentSms;
|
||||
// Check a silent sms number
|
||||
SilentSms.checkSilentModeFor(1234).then(function (isSilent) {
|
||||
assert.isTrue(isSilent);
|
||||
}).then(SilentSms.checkSilentModeFor.bind(null, 999))
|
||||
.then(function (isSilent) {
|
||||
assert.isFalse(isSilent);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
726
gaia/apps/sms/views/shared/test/unit/smil_test.js
Executable file
726
gaia/apps/sms/views/shared/test/unit/smil_test.js
Executable file
|
|
@ -0,0 +1,726 @@
|
|||
/*global SMIL, MocksHelper,
|
||||
AssetsHelper,
|
||||
Promise
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/js/utils.js');
|
||||
require('/views/shared/test/unit/mock_utils.js');
|
||||
require('/views/shared/js/wbmp.js');
|
||||
require('/views/shared/js/smil.js');
|
||||
|
||||
var mocksHelperForSMIL = new MocksHelper([
|
||||
'Utils'
|
||||
]);
|
||||
|
||||
mocksHelperForSMIL.init();
|
||||
|
||||
suite('SMIL', function() {
|
||||
var testImageBlob;
|
||||
var testAudioBlob;
|
||||
var testVideoBlob;
|
||||
var testWbmpBlob;
|
||||
var testContactBlob;
|
||||
suiteSetup(function smil_suiteSetup(done) {
|
||||
mocksHelperForSMIL.suiteSetup();
|
||||
|
||||
var mediafolder = '/views/shared/test/unit/media';
|
||||
var blobPromises = [
|
||||
AssetsHelper.generateImageBlob(300, 300, 'image/jpeg', 0.25).then(
|
||||
(blob) => testImageBlob = blob
|
||||
),
|
||||
AssetsHelper.loadFileBlob(`${mediafolder}/grid.wbmp`).then(
|
||||
(blob) => testWbmpBlob = blob
|
||||
),
|
||||
AssetsHelper.loadFileBlob(`${mediafolder}/audio.oga`).then(
|
||||
(blob) => testAudioBlob = blob
|
||||
),
|
||||
AssetsHelper.loadFileBlob(`${mediafolder}/video.ogv`).then(
|
||||
(blob) => testVideoBlob = blob
|
||||
),
|
||||
AssetsHelper.loadFileBlob(`${mediafolder}/contacts.vcf`).then(
|
||||
(blob) => testContactBlob = blob
|
||||
)
|
||||
];
|
||||
|
||||
Promise.all(blobPromises).then(() => done(), done);
|
||||
});
|
||||
suiteTeardown(function() {
|
||||
mocksHelperForSMIL.suiteTeardown();
|
||||
});
|
||||
setup(function() {
|
||||
mocksHelperForSMIL.setup();
|
||||
});
|
||||
teardown(function() {
|
||||
mocksHelperForSMIL.teardown();
|
||||
});
|
||||
suite('SMIL.parse', function() {
|
||||
test('Text only message without smil', function(done) {
|
||||
var text = ['Test text', 'omg!'];
|
||||
// minimal fake data for text only message without smil
|
||||
var messageData = {
|
||||
attachments: [
|
||||
{content: new Blob([text[0]], {type: 'text/plain'})},
|
||||
{content: new Blob([text[1]], {type: 'text/plain'})}
|
||||
]
|
||||
};
|
||||
SMIL.parse(messageData).then((output) => {
|
||||
// one slide for each attachment is returned
|
||||
assert.equal(output.length, 2);
|
||||
// the text has not been joined in one slide
|
||||
assert.equal(output[0].text, text[0]);
|
||||
assert.equal(output[1].text, text[1]);
|
||||
}).then(done, done);
|
||||
});
|
||||
test('Text and image message without smil', function(done) {
|
||||
var text = ['Test above image', 'Text below image'];
|
||||
var messageData = {
|
||||
attachments: [
|
||||
{content: new Blob([text[0]], {type: 'text/plain'})},
|
||||
{
|
||||
content: testImageBlob,
|
||||
location: 'example.jpg'
|
||||
},
|
||||
{content: new Blob([text[1]], {type: 'text/plain'})},
|
||||
]
|
||||
};
|
||||
SMIL.parse(messageData).then((output) => {
|
||||
// three slides returned
|
||||
assert.equal(output.length, 3);
|
||||
// the order of the attached components should be respected, the text
|
||||
// is located on two different slides (not joined)
|
||||
assert.equal(output[0].text, text[0]);
|
||||
assert.equal(output[1].blob, testImageBlob);
|
||||
assert.equal(output[1].name, 'example.jpg');
|
||||
assert.equal(output[2].text, text[1]);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Image only message without smil', function(done) {
|
||||
// minimal fake data for text only message without smil
|
||||
var messageData = {
|
||||
attachments: [
|
||||
{
|
||||
content: testImageBlob,
|
||||
location: 'example.jpg'
|
||||
}
|
||||
]
|
||||
};
|
||||
SMIL.parse(messageData).then((output) => {
|
||||
// one slide returned
|
||||
assert.equal(output.length, 1);
|
||||
// no text in this slide !
|
||||
assert.ok(!output[0].text);
|
||||
assert.equal(output[0].blob, testImageBlob);
|
||||
assert.equal(output[0].name, 'example.jpg');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Minimal SMIL doc', function(done) {
|
||||
var testText = 'Testing 1 2 3';
|
||||
var message = {
|
||||
smil: '<smil><body><par><img src="example.jpg"/>' +
|
||||
'<text src="text1"/></par></body></smil>',
|
||||
attachments: [{
|
||||
location: 'text1',
|
||||
content: new Blob([testText], {type: 'text/plain'})
|
||||
},{
|
||||
location: 'example.jpg',
|
||||
content: testImageBlob
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].text, testText);
|
||||
assert.equal(output[0].blob, testImageBlob);
|
||||
assert.equal(output[0].name, 'example.jpg');
|
||||
}).then(done, done);
|
||||
});
|
||||
test('SMIL doc with 2 text only slides', function(done) {
|
||||
var text = ['Test slide 1', 'Test slide 2'];
|
||||
var message = {
|
||||
smil: '<smil><body><par><text src="cid:1"/></par>' +
|
||||
'<par><text src="cid:2"/></par></body></smil>',
|
||||
attachments: [{
|
||||
id: '<1>',
|
||||
location: 'text1',
|
||||
content: new Blob([text[0]], {type: 'text/plain'})
|
||||
},{
|
||||
id: '<2>',
|
||||
location: 'text2',
|
||||
content: new Blob([text[1]], {type: 'text/plain'})
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output.length, 2);
|
||||
assert.equal(output[0].text, text[0]);
|
||||
assert.equal(output[1].text, text[1]);
|
||||
}).then(done, done);
|
||||
});
|
||||
test('SMIL doc with cid: prefixes on src', function(done) {
|
||||
var testText = 'Testing 1 2 3';
|
||||
var message = {
|
||||
smil: '<smil><body><par><img src="cid:23"/>' +
|
||||
'<text src="cid:text1"/></par></body></smil>',
|
||||
attachments: [{
|
||||
location: 'text1',
|
||||
content: new Blob([testText], {type: 'text/plain'})
|
||||
},{
|
||||
id: '<23>',
|
||||
location: 'example.jpg',
|
||||
content: testImageBlob
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].text, testText);
|
||||
assert.equal(output[0].blob, testImageBlob);
|
||||
assert.equal(output[0].name, 'example.jpg');
|
||||
}).then(done, done);
|
||||
});
|
||||
test('SMIL doc with cid: prefixes on src and no location', function(done) {
|
||||
// iphone!
|
||||
// parsed as a non smil message
|
||||
var testText = 'Testing 1 2 3';
|
||||
var message = {
|
||||
smil: '<smil><body><par><img src="cid:23"/>' +
|
||||
'<text src="cid:1"/></par></body></smil>',
|
||||
attachments: [{
|
||||
location: '<1>',
|
||||
content: new Blob([testText], {type: 'text/plain'})
|
||||
},{
|
||||
id: '<23>',
|
||||
content: testImageBlob
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].text, testText);
|
||||
assert.equal(output[1].blob, testImageBlob);
|
||||
assert.isUndefined(output[1].name, 'name is undefined');
|
||||
}).then(done, done);
|
||||
});
|
||||
test('SMIL doc with cid: prefixes on src pointing to ids', function(done) {
|
||||
var testText = 'Testing 1 2 3';
|
||||
var message = {
|
||||
smil: '<smil><body><par><img src="cid:2"/>' +
|
||||
'<text src="cid:1"/></par></body></smil>',
|
||||
attachments: [{
|
||||
id: '<1>',
|
||||
location: 'text1',
|
||||
content: new Blob([testText], {type: 'text/plain'})
|
||||
},{
|
||||
id: '<2>',
|
||||
location: 'example.jpg',
|
||||
content: testImageBlob
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].text, testText);
|
||||
assert.equal(output[0].blob, testImageBlob);
|
||||
assert.equal(output[0].name, 'example.jpg');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('SMIL doc with bad cid: references', function(done) {
|
||||
var testText = 'Testing 1 2 3';
|
||||
var message = {
|
||||
smil: '<smil><body><par><text src="cid:1"/>' +
|
||||
'<img src="cid:20"/></par></body></smil>',
|
||||
attachments: [{
|
||||
id: '<1>',
|
||||
location: 'text1',
|
||||
content: new Blob([testText], {type: 'text/plain'})
|
||||
},{
|
||||
id: '<2>',
|
||||
location: 'example.jpg',
|
||||
content: testImageBlob
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].text, testText);
|
||||
assert.equal(output[1].blob, testImageBlob);
|
||||
assert.equal(output[1].name, 'example.jpg');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
suite('SMIL doc: audio and image on same <par>', function() {
|
||||
var testText = 'Testing 1 2 3';
|
||||
var result;
|
||||
setup(function(done) {
|
||||
var testText = 'Testing 1 2 3';
|
||||
var message = {
|
||||
smil: '<smil><body><par><text src="cid:1"/>' +
|
||||
'<audio src="cid:2"/><img src="cid:3"/>' +
|
||||
'</par></body></smil>',
|
||||
attachments: [{
|
||||
id: '<1>',
|
||||
location: 'text1',
|
||||
content: new Blob([testText], {type: 'text/plain'})
|
||||
}, {
|
||||
id: '<2>',
|
||||
location: 'audio.oga',
|
||||
content: testAudioBlob
|
||||
}, {
|
||||
id: '<3>',
|
||||
location: 'example.jpg',
|
||||
content: testImageBlob
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
result = output;
|
||||
}).then(done, done);
|
||||
});
|
||||
test('Results in two "slides"', function() {
|
||||
assert.equal(result.length, 2);
|
||||
});
|
||||
test('First slide does not have text', function() {
|
||||
assert.equal(result[0].text, undefined);
|
||||
});
|
||||
test('First slide contains audio', function() {
|
||||
assert.equal(result[0].name, 'audio.oga');
|
||||
assert.equal(result[0].blob, testAudioBlob);
|
||||
});
|
||||
test('Second slide contains text', function() {
|
||||
assert.equal(result[1].text, testText);
|
||||
});
|
||||
test('Second slide contains image', function() {
|
||||
assert.equal(result[1].name, 'example.jpg');
|
||||
assert.equal(result[1].blob, testImageBlob);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('empty SMIL doc with attachments', function(done) {
|
||||
var testText = 'Testing 1 2 3';
|
||||
var message = {
|
||||
smil: '<smil><body></body></smil>',
|
||||
attachments: [{
|
||||
id: '<1>',
|
||||
location: 'text1',
|
||||
content: new Blob([testText], {type: 'text/plain'})
|
||||
},{
|
||||
id: '<2>',
|
||||
location: 'example.jpg',
|
||||
content: testImageBlob
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].text, testText);
|
||||
assert.equal(output[1].blob, testImageBlob);
|
||||
assert.equal(output[1].name, 'example.jpg');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('empty SMIL doc with attachments and no location', function(done) {
|
||||
var testText = 'Testing 1 2 3';
|
||||
var message = {
|
||||
smil: '<smil><body></body></smil>',
|
||||
attachments: [{
|
||||
id: '<1>',
|
||||
content: new Blob([testText], {type: 'text/plain'})
|
||||
},{
|
||||
id: '<2>',
|
||||
content: testImageBlob
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].text, testText);
|
||||
assert.equal(output[1].blob, testImageBlob);
|
||||
assert.isUndefined(output[1].name, 'name is undefined');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Type of attachment is WBMP format', function(done) {
|
||||
var message = {
|
||||
smil: '<smil><body><par><img src="grid.wbmp"/>' +
|
||||
'</par></body></smil>',
|
||||
attachments: [{
|
||||
location: 'grid.wbmp',
|
||||
content: testWbmpBlob
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].blob.type, 'image/png');
|
||||
assert.equal(output[0].name, 'grid.png');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
suite('Type of attachment is vcard format', function() {
|
||||
var attachments;
|
||||
var testText = {
|
||||
location: 'text1',
|
||||
content: new Blob(['test Text'], {type: 'text/plain'})
|
||||
};
|
||||
|
||||
setup(function() {
|
||||
attachments = [{
|
||||
location: 'contacts.vcf',
|
||||
content: testContactBlob
|
||||
}];
|
||||
});
|
||||
|
||||
test('only contact in attachment', function(done) {
|
||||
var message = {
|
||||
smil: '<smil><body><par><ref src="contacts.vcf"/>' +
|
||||
'</par></body></smil>',
|
||||
attachments: attachments
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].blob, testContactBlob);
|
||||
assert.equal(output[0].name, 'contacts.vcf');
|
||||
assert.isUndefined(output[0].text);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('contact with text', function(done) {
|
||||
attachments.push(testText);
|
||||
var message = {
|
||||
smil: '<smil><body><par><text src="' + testText.location + '"/>' +
|
||||
'<ref src="contacts.vcf"/></par></body></smil>',
|
||||
attachments: attachments
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].blob, testContactBlob);
|
||||
assert.equal(output[0].name, 'contacts.vcf');
|
||||
assert.equal(output[0].text, 'test Text');
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('contact attachment with no smil', function(done) {
|
||||
var message = {
|
||||
attachments: attachments
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].blob, testContactBlob);
|
||||
assert.equal(output[0].name, 'contacts.vcf');
|
||||
assert.isUndefined(output[0].text);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('contact attachment/text with no smil', function(done) {
|
||||
attachments.push(testText);
|
||||
var message = {
|
||||
attachments: attachments
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].blob, testContactBlob);
|
||||
assert.equal(output[0].name, 'contacts.vcf');
|
||||
assert.equal(output[1].text, 'test Text');
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
test('Attachment count and elements in SMIL is unmatched', function(done) {
|
||||
var testText = 'Test text';
|
||||
var message = {
|
||||
smil: '<smil><body><par><text src="cid:1"/>' +
|
||||
'</par></body></smil>',
|
||||
attachments: [{
|
||||
id: '<1>',
|
||||
location: 'text1',
|
||||
content: new Blob([testText], {type: 'text/plain'})
|
||||
},{
|
||||
id: '<2>',
|
||||
location: 'example.jpg',
|
||||
content: testImageBlob
|
||||
}]
|
||||
};
|
||||
SMIL.parse(message).then((output) => {
|
||||
assert.equal(output[0].text, testText);
|
||||
assert.equal(output[1].blob, testImageBlob);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
suite('SMIL.generate', function() {
|
||||
test('Text only message', function(done) {
|
||||
var smilTest = [{
|
||||
text: 'This is a test of the SMIL generate method'
|
||||
}];
|
||||
var output = SMIL.generate(smilTest);
|
||||
var doc = (new DOMParser())
|
||||
.parseFromString(output.smil, 'application/xml')
|
||||
.documentElement;
|
||||
|
||||
// only one attachment
|
||||
assert.equal(output.attachments.length, 1);
|
||||
|
||||
// only one <par> tag in the smil output
|
||||
assert.equal(doc.getElementsByTagName('par').length, 1);
|
||||
|
||||
// check that the content of the text blob is what we said it should be
|
||||
var textReader = new FileReader();
|
||||
textReader.onload = function(event) {
|
||||
assert.equal(event.target.result, smilTest[0].text);
|
||||
done();
|
||||
};
|
||||
textReader.readAsText(output.attachments[0].content);
|
||||
|
||||
});
|
||||
|
||||
test('Message with image and text', function() {
|
||||
var smilTest = [{
|
||||
text: 'Testing a caption',
|
||||
name: 'kitten-450.jpg',
|
||||
blob: testImageBlob
|
||||
}];
|
||||
var output = SMIL.generate(smilTest);
|
||||
var doc = (new DOMParser())
|
||||
.parseFromString(output.smil, 'application/xml')
|
||||
.documentElement;
|
||||
|
||||
// two attachments (text and image)
|
||||
assert.equal(output.attachments.length, 2);
|
||||
|
||||
// only one <par> tag
|
||||
assert.equal(doc.querySelectorAll('par').length, 1);
|
||||
|
||||
// the img is before the text
|
||||
assert.equal(doc.querySelectorAll('img + text').length, 1);
|
||||
|
||||
assert.equal(doc.querySelector('text').getAttribute('region'), 'Text');
|
||||
assert.equal(doc.querySelector('img').getAttribute('region'), 'Image');
|
||||
});
|
||||
|
||||
test('Message with vcard and text', function() {
|
||||
var smilTest = [{
|
||||
text: 'Testing a caption',
|
||||
name: 'contact.vcf',
|
||||
blob: testContactBlob
|
||||
}];
|
||||
var output = SMIL.generate(smilTest);
|
||||
var doc = (new DOMParser())
|
||||
.parseFromString(output.smil, 'application/xml')
|
||||
.documentElement;
|
||||
|
||||
// two attachments (text and contact)
|
||||
assert.equal(output.attachments.length, 2);
|
||||
|
||||
// only one <par> tag
|
||||
assert.equal(doc.querySelectorAll('par').length, 1);
|
||||
|
||||
// the ref is before the text
|
||||
assert.equal(doc.querySelectorAll('ref + text').length, 1);
|
||||
|
||||
assert.equal(doc.querySelector('text').getAttribute('region'), 'Text');
|
||||
assert.isNull(doc.querySelector('ref').getAttribute('region'));
|
||||
});
|
||||
|
||||
test('Message with only vcard', function() {
|
||||
var smilTest = [{
|
||||
name: 'contact.vcf',
|
||||
blob: testContactBlob
|
||||
}];
|
||||
var output = SMIL.generate(smilTest);
|
||||
var doc = (new DOMParser())
|
||||
.parseFromString(output.smil, 'application/xml')
|
||||
.documentElement;
|
||||
|
||||
// one attachment (contact)
|
||||
assert.equal(output.attachments.length, 1);
|
||||
|
||||
// only one <par> tag
|
||||
assert.equal(doc.querySelectorAll('par').length, 1);
|
||||
|
||||
// the ref appears
|
||||
assert.equal(doc.querySelectorAll('ref').length, 1);
|
||||
|
||||
assert.isNull(doc.querySelector('text'));
|
||||
assert.isNull(doc.querySelector('ref').getAttribute('region'));
|
||||
});
|
||||
|
||||
test('Message with path as filename', function() {
|
||||
var smilTest = [{
|
||||
text: 'Testing a caption',
|
||||
name: 'SDCARD/DCIM/kitten-450.jpg',
|
||||
blob: testImageBlob
|
||||
}];
|
||||
var output = SMIL.generate(smilTest);
|
||||
|
||||
assert.equal(output.attachments.length, 2);
|
||||
|
||||
assert.equal(output.attachments[0].location, 'kitten-450.jpg');
|
||||
});
|
||||
|
||||
test('Message with same filename from different path', function() {
|
||||
var smilTest = [{
|
||||
text: 'Testing a caption',
|
||||
name: 'SDCARD/DCIM/kitten-450.jpg',
|
||||
blob: testImageBlob
|
||||
}, {
|
||||
text: 'Testing a caption',
|
||||
name: 'SDCARD/DCIM23/kitten-450.jpg',
|
||||
blob: testImageBlob
|
||||
}];
|
||||
var output = SMIL.generate(smilTest);
|
||||
assert.equal(output.attachments.length, 4);
|
||||
assert.equal(output.attachments[0].location, 'kitten-450.jpg');
|
||||
assert.equal(output.attachments[2].location, 'kitten-450_2.jpg');
|
||||
});
|
||||
|
||||
suite('SMIL filename: length limitations', function() {
|
||||
var bareString, extString;
|
||||
bareString = 'dfjaksdhfkasjdhfaksjdfhksjdahfaksjdhfdfsfffff';
|
||||
extString = 'dfjaksdhfkasjdhfaksjdfhksjdahfaksjdhfdfsf';
|
||||
test('Filenames truncated', function() {
|
||||
var smilTest = [{
|
||||
text: 'Truncate filename',
|
||||
name: bareString,
|
||||
blob: testImageBlob
|
||||
},{
|
||||
text: 'With extension',
|
||||
name: extString + '.jpg',
|
||||
blob: testImageBlob
|
||||
}];
|
||||
var output = SMIL.generate(smilTest);
|
||||
assert.equal(
|
||||
output.attachments[0].location,
|
||||
bareString.slice(0, 40)
|
||||
);
|
||||
assert.equal(
|
||||
output.attachments[2].location,
|
||||
extString.slice(0, 40 - 4) + '.jpg'
|
||||
);
|
||||
assert.ok(output.attachments.every(function(elem) {
|
||||
return elem.location.length <= 40;
|
||||
}));
|
||||
});
|
||||
suite('Clashing filenames also truncated', function() {
|
||||
var smilTest, bare, extension;
|
||||
var bareString = 'dfjaksdhfkasjdhfaksjdfhksjdahfaksjdhfdf';
|
||||
var extString = bareString.slice(0, -3);
|
||||
setup(function() {
|
||||
smilTest = [{
|
||||
text: 'Setup clash',
|
||||
name: bareString,
|
||||
blob: testImageBlob
|
||||
},{
|
||||
text: 'Testing first clash',
|
||||
name: bareString,
|
||||
blob: testImageBlob
|
||||
},{
|
||||
text: 'Testing second clash',
|
||||
name: bareString,
|
||||
blob: testImageBlob
|
||||
}];
|
||||
bare = SMIL.generate(smilTest);
|
||||
smilTest.forEach(function(elem) {
|
||||
elem.name = extString + '.jpg';
|
||||
});
|
||||
extension = SMIL.generate(smilTest);
|
||||
});
|
||||
test('First name unchanged', function() {
|
||||
assert.equal(
|
||||
bare.attachments[0].location,
|
||||
bareString
|
||||
);
|
||||
assert.equal(
|
||||
extension.attachments[0].location,
|
||||
extString + '.jpg'
|
||||
);
|
||||
});
|
||||
test('2nd name shortened due to potential marker length', function() {
|
||||
assert.equal(
|
||||
bare.attachments[2].location,
|
||||
bareString.slice(0, -1)
|
||||
);
|
||||
assert.equal(
|
||||
extension.attachments[2].location,
|
||||
extString.slice(0, -1) + '.jpg'
|
||||
);
|
||||
});
|
||||
test('Third name shortened/expanded as needed', function() {
|
||||
assert.equal(
|
||||
bare.attachments[4].location,
|
||||
bareString.slice(0, -1) + '_2'
|
||||
);
|
||||
assert.equal(
|
||||
extension.attachments[4].location,
|
||||
extString.slice(0, -2) + '.jpg'
|
||||
);
|
||||
});
|
||||
test('All combinations <= 40 characters', function() {
|
||||
assert.ok(bare.attachments.every(function(elem) {
|
||||
return elem.location.length <= 40;
|
||||
}));
|
||||
assert.ok(extension.attachments.every(function(elem) {
|
||||
return elem.location.length <= 40;
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('Message with duplicate filename', function() {
|
||||
var smilTest = [{
|
||||
name: 'kitten-450.jpg',
|
||||
blob: testImageBlob
|
||||
}, {
|
||||
name: 'kitten-450.jpg',
|
||||
blob: testImageBlob
|
||||
}, {
|
||||
name: 'kitten-450.jpg',
|
||||
blob: testImageBlob
|
||||
}];
|
||||
var output = SMIL.generate(smilTest);
|
||||
var doc = (new DOMParser())
|
||||
.parseFromString(output.smil, 'application/xml')
|
||||
.documentElement;
|
||||
|
||||
assert.equal(output.attachments.length, 3);
|
||||
var images = Array.prototype.slice.call(doc.querySelectorAll('img'));
|
||||
assert.equal(images.length, 3);
|
||||
|
||||
// ensure the file names have been properly created
|
||||
var filenames = [
|
||||
'kitten-450.jpg',
|
||||
'kitten-450_2.jpg',
|
||||
'kitten-450_3.jpg'
|
||||
];
|
||||
|
||||
assert.deepEqual(output.attachments.map(function(attachment) {
|
||||
return attachment.location;
|
||||
}), filenames, 'List of filenames matches attachments');
|
||||
|
||||
assert.deepEqual(images.map(function(img) {
|
||||
return img.getAttribute('src');
|
||||
}), filenames, 'List of filenames matches <img> tags');
|
||||
|
||||
});
|
||||
|
||||
test('Message with non-ascii filenames', function() {
|
||||
var smilTest = [{
|
||||
name: 'kitten♥-450.jpg',
|
||||
blob: testImageBlob
|
||||
}, {
|
||||
// 5 replaced chars
|
||||
name: '새끼고양이.jpg',
|
||||
blob: testImageBlob
|
||||
}, {
|
||||
// 5 replaced chars
|
||||
name: 'ἀρετή.jpg',
|
||||
blob: testImageBlob
|
||||
}, {
|
||||
// testing non-replaced chars
|
||||
name: 'abzABZ019_#.()?&%-.jpg',
|
||||
blob: testImageBlob
|
||||
}, {
|
||||
// quotes MUST be replaced - filename content is used in xml
|
||||
name: '"\'.jpg',
|
||||
blob: testImageBlob
|
||||
}];
|
||||
var output = SMIL.generate(smilTest);
|
||||
assert.equal(output.attachments.length, 5);
|
||||
assert.equal(output.attachments[0].location, 'kitten#-450.jpg');
|
||||
|
||||
// output from the next two also tests a clash after replace
|
||||
assert.equal(output.attachments[1].location, '#####.jpg');
|
||||
assert.equal(output.attachments[2].location, '#####_2.jpg');
|
||||
|
||||
// this one has nothing to replace:
|
||||
assert.equal(output.attachments[3].location, smilTest[3].name);
|
||||
|
||||
// quotes must be replaced
|
||||
assert.equal(output.attachments[4].location, '##.jpg');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
1003
gaia/apps/sms/views/shared/test/unit/sms_test.js
Executable file
1003
gaia/apps/sms/views/shared/test/unit/sms_test.js
Executable file
File diff suppressed because it is too large
Load diff
159
gaia/apps/sms/views/shared/test/unit/startup_test.js
Executable file
159
gaia/apps/sms/views/shared/test/unit/startup_test.js
Executable file
|
|
@ -0,0 +1,159 @@
|
|||
/*global App,
|
||||
Drafts,
|
||||
InboxView,
|
||||
LazyLoader,
|
||||
MessageManager,
|
||||
MocksHelper,
|
||||
Navigation,
|
||||
Utils
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/js/app.js');
|
||||
|
||||
require('/services/test/unit/mock_message_manager.js');
|
||||
require('/services/test/unit/mock_drafts.js');
|
||||
require('/views/shared/test/unit/mock_navigation.js');
|
||||
require('/views/shared/test/unit/mock_inbox.js');
|
||||
require('/views/shared/js/utils.js');
|
||||
require('/views/shared/test/unit/mock_utils.js');
|
||||
|
||||
require('/shared/test/unit/mocks/mock_lazy_loader.js');
|
||||
|
||||
var mocksHelper = new MocksHelper([
|
||||
'Drafts',
|
||||
'MessageManager',
|
||||
'LazyLoader',
|
||||
'Navigation',
|
||||
'InboxView',
|
||||
'Utils'
|
||||
]).init();
|
||||
|
||||
suite('Startup >', function() {
|
||||
var originalMozHasPendingMessage;
|
||||
|
||||
mocksHelper.attachTestHelpers();
|
||||
|
||||
suiteSetup(function(done) {
|
||||
originalMozHasPendingMessage = navigator.mozHasPendingMessage;
|
||||
navigator.mozHasPendingMessage = () => {};
|
||||
|
||||
sinon.stub(window, 'addEventListener');
|
||||
require('/views/shared/js/startup.js', done);
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
navigator.mozHasPendingMessage = originalMozHasPendingMessage;
|
||||
window.addEventListener.restore();
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sinon.stub(window, 'dispatchEvent');
|
||||
this.sinon.stub(window, 'removeEventListener');
|
||||
this.sinon.stub(navigator, 'mozHasPendingMessage').returns(false);
|
||||
this.sinon.stub(LazyLoader, 'load').returns(Promise.reject());
|
||||
this.sinon.stub(MessageManager, 'init');
|
||||
this.sinon.stub(Navigation, 'init');
|
||||
this.sinon.stub(InboxView, 'init');
|
||||
this.sinon.spy(InboxView, 'renderThreads');
|
||||
this.sinon.stub(InboxView, 'once');
|
||||
this.sinon.stub(Navigation, 'once');
|
||||
this.sinon.stub(Navigation, 'isDefaultPanel');
|
||||
this.sinon.stub(App, 'setReady');
|
||||
this.sinon.spy(Utils, 'initializeShimHost');
|
||||
this.sinon.stub(Drafts, 'init');
|
||||
});
|
||||
|
||||
test('if target panel is default one', function() {
|
||||
Navigation.isDefaultPanel.returns(true);
|
||||
|
||||
window.addEventListener.withArgs('DOMContentLoaded').yield();
|
||||
|
||||
sinon.assert.calledWith(Utils.initializeShimHost, App.instanceId);
|
||||
|
||||
// Navigate to Inbox immediately.
|
||||
sinon.assert.callOrder(
|
||||
Utils.initializeShimHost,
|
||||
MessageManager.init,
|
||||
Drafts.init,
|
||||
InboxView.init,
|
||||
Navigation.init,
|
||||
InboxView.renderThreads
|
||||
);
|
||||
sinon.assert.notCalled(LazyLoader.load);
|
||||
|
||||
// First page of threads loaded
|
||||
InboxView.once.withArgs('visually-loaded').yield();
|
||||
|
||||
// Lazy load the rest of scripts only once first page of threads is loaded
|
||||
sinon.assert.calledOnce(LazyLoader.load);
|
||||
});
|
||||
|
||||
test('if first panel is not default one', function() {
|
||||
Navigation.isDefaultPanel.returns(false);
|
||||
|
||||
window.addEventListener.withArgs('DOMContentLoaded').yield();
|
||||
|
||||
sinon.assert.calledWith(Utils.initializeShimHost, App.instanceId);
|
||||
|
||||
sinon.assert.callOrder(
|
||||
Utils.initializeShimHost,
|
||||
MessageManager.init,
|
||||
InboxView.init,
|
||||
Navigation.init,
|
||||
LazyLoader.load
|
||||
);
|
||||
sinon.assert.notCalled(InboxView.renderThreads);
|
||||
|
||||
sinon.assert.calledOnce(LazyLoader.load);
|
||||
|
||||
// Threads should start rendering only once target panel is ready.
|
||||
Navigation.once.withArgs('navigated').yield();
|
||||
|
||||
// Now we have time and resources to render threads.
|
||||
sinon.assert.calledOnce(InboxView.renderThreads);
|
||||
|
||||
// App is marked is ready only when all threads are loaded.
|
||||
sinon.assert.notCalled(App.setReady);
|
||||
|
||||
InboxView.once.withArgs('fully-loaded').yield();
|
||||
|
||||
sinon.assert.calledOnce(App.setReady);
|
||||
});
|
||||
|
||||
['notification', 'activity'].forEach((eventName) => {
|
||||
test('if has pending "' + eventName + '" system message', function() {
|
||||
Navigation.isDefaultPanel.returns(true);
|
||||
navigator.mozHasPendingMessage.withArgs(eventName).returns(true);
|
||||
|
||||
window.addEventListener.withArgs('DOMContentLoaded').yield();
|
||||
|
||||
sinon.assert.calledWith(Utils.initializeShimHost, App.instanceId);
|
||||
|
||||
sinon.assert.callOrder(
|
||||
Utils.initializeShimHost,
|
||||
MessageManager.init,
|
||||
InboxView.init,
|
||||
Navigation.init,
|
||||
LazyLoader.load
|
||||
);
|
||||
sinon.assert.notCalled(InboxView.renderThreads);
|
||||
|
||||
sinon.assert.calledOnce(LazyLoader.load);
|
||||
|
||||
// Threads should start rendering only once target panel is ready.
|
||||
Navigation.once.withArgs('navigated').yield();
|
||||
|
||||
// Now we have time and resources to render threads.
|
||||
sinon.assert.calledOnce(InboxView.renderThreads);
|
||||
|
||||
// App is marked is ready only when all threads are loaded.
|
||||
sinon.assert.notCalled(App.setReady);
|
||||
|
||||
InboxView.once.withArgs('fully-loaded').yield();
|
||||
|
||||
sinon.assert.calledOnce(App.setReady);
|
||||
});
|
||||
});
|
||||
});
|
||||
794
gaia/apps/sms/views/shared/test/unit/system_message_handler_test.js
Executable file
794
gaia/apps/sms/views/shared/test/unit/system_message_handler_test.js
Executable file
|
|
@ -0,0 +1,794 @@
|
|||
/*global Contacts,
|
||||
MessageManager,
|
||||
MockContact,
|
||||
MocksHelper,
|
||||
MockMessages,
|
||||
Navigation,
|
||||
NotificationHelper,
|
||||
Notify,
|
||||
Settings,
|
||||
SilentSms,
|
||||
SMIL,
|
||||
SystemMessageHandler,
|
||||
Utils
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_notification_helper.js');
|
||||
|
||||
require('/views/shared/test/unit/mock_contact.js');
|
||||
require('/views/shared/test/unit/mock_contacts.js');
|
||||
require('/views/shared/test/unit/mock_messages.js');
|
||||
require('/services/test/unit/mock_message_manager.js');
|
||||
require('/views/shared/test/unit/mock_settings.js');
|
||||
require('/views/shared/test/unit/mock_notify.js');
|
||||
require('/views/shared/test/unit/mock_navigation.js');
|
||||
require('/views/shared/test/unit/mock_silent_sms.js');
|
||||
require('/views/shared/test/unit/mock_smil.js');
|
||||
|
||||
require('/views/shared/js/utils.js');
|
||||
require('/views/shared/test/unit/mock_utils.js');
|
||||
|
||||
require('/views/shared/js/system_message_handler.js');
|
||||
|
||||
var mocksHelperForActivityHandler = new MocksHelper([
|
||||
'Contacts',
|
||||
'MessageManager',
|
||||
'Navigation',
|
||||
'NotificationHelper',
|
||||
'Notify',
|
||||
'Settings',
|
||||
'SilentSms',
|
||||
'SMIL',
|
||||
'Utils'
|
||||
]).init();
|
||||
|
||||
suite('SystemMessageHandler', function() {
|
||||
mocksHelperForActivityHandler.attachTestHelpers();
|
||||
|
||||
var isDocumentHidden;
|
||||
|
||||
var navigatorMocks = new Map([
|
||||
['mozSetMessageHandler', () => {}],
|
||||
['requestWakeLock', () => {}],
|
||||
['mozApps', { launch() {} }]
|
||||
]);
|
||||
|
||||
suiteSetup(function() {
|
||||
navigatorMocks.forEach((mock, key) => {
|
||||
if (!(key in navigator)) {
|
||||
navigator[key] = mock;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(document, 'hidden', {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
return isDocumentHidden;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
delete document.hidden;
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sinon.stub(Utils, 'alert').returns(Promise.resolve());
|
||||
|
||||
isDocumentHidden = false;
|
||||
this.sinon.stub(document, 'addEventListener');
|
||||
|
||||
SystemMessageHandler.init();
|
||||
});
|
||||
|
||||
test('init()', function() {
|
||||
this.sinon.stub(navigator, 'mozSetMessageHandler');
|
||||
|
||||
SystemMessageHandler.init();
|
||||
|
||||
sinon.assert.calledTwice(window.navigator.mozSetMessageHandler);
|
||||
sinon.assert.calledWith(
|
||||
window.navigator.mozSetMessageHandler, 'sms-received'
|
||||
);
|
||||
sinon.assert.calledWith(
|
||||
window.navigator.mozSetMessageHandler, 'notification'
|
||||
);
|
||||
});
|
||||
|
||||
suite('sms-received system message', function() {
|
||||
var appStub, wakeLockStub, notificationStub;
|
||||
|
||||
function assertWakeLockIsRequested() {
|
||||
sinon.assert.calledOnce(navigator.requestWakeLock);
|
||||
sinon.assert.calledWith(navigator.requestWakeLock, 'cpu');
|
||||
|
||||
// We shouldn't release wake lock until we completely handled new message.
|
||||
sinon.assert.notCalled(wakeLockStub.unlock);
|
||||
}
|
||||
|
||||
function assertWakeLockIsReleased() {
|
||||
sinon.assert.calledOnce(wakeLockStub.unlock);
|
||||
}
|
||||
|
||||
function assertUserIsNotified() {
|
||||
sinon.assert.calledOnce(Notify.ringtone);
|
||||
sinon.assert.calledOnce(Notify.vibrate);
|
||||
}
|
||||
|
||||
function assertUserIsNotNotified() {
|
||||
sinon.assert.notCalled(Notify.ringtone);
|
||||
sinon.assert.notCalled(Notify.vibrate);
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
appStub = sinon.stub({ launch() {} });
|
||||
|
||||
wakeLockStub = sinon.stub({
|
||||
unlock: () => {}
|
||||
});
|
||||
|
||||
notificationStub = sinon.stub({
|
||||
addEventListener() {},
|
||||
close() {}
|
||||
});
|
||||
|
||||
this.sinon.stub(navigator.mozApps, 'getSelf').returns(
|
||||
Promise.resolve(appStub)
|
||||
);
|
||||
this.sinon.stub(navigator, 'requestWakeLock').returns(wakeLockStub);
|
||||
this.sinon.spy(Notify, 'ringtone');
|
||||
this.sinon.spy(Notify, 'vibrate');
|
||||
this.sinon.stub(SilentSms, 'checkSilentModeFor').returns(
|
||||
Promise.resolve(false)
|
||||
);
|
||||
this.sinon.stub(Navigation, 'isCurrentPanel').returns(false);
|
||||
this.sinon.stub(Navigation, 'toPanel').returns(Promise.resolve());
|
||||
this.sinon.stub(NotificationHelper, 'send').returns(notificationStub);
|
||||
|
||||
this.sinon.stub(NotificationHelper, 'getIconURI').returns(null);
|
||||
NotificationHelper.getIconURI.withArgs(appStub).returns('iconURI');
|
||||
|
||||
this.sinon.stub(Contacts, 'findByAddress').returns(
|
||||
Promise.resolve([])
|
||||
);
|
||||
});
|
||||
|
||||
suite('received message from silenced number', function() {
|
||||
setup(function() {
|
||||
SilentSms.checkSilentModeFor.returns(Promise.resolve(true));
|
||||
});
|
||||
|
||||
test('does not notify user in any way', function(done) {
|
||||
var handlingPromise = SystemMessageHandler.onSmsReceivedSystemMessage(
|
||||
MockMessages.sms()
|
||||
);
|
||||
|
||||
assertWakeLockIsRequested();
|
||||
|
||||
handlingPromise.then(() => {
|
||||
assertUserIsNotNotified();
|
||||
assertWakeLockIsReleased();
|
||||
|
||||
sinon.assert.notCalled(NotificationHelper.send);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('received class-0 message', function() {
|
||||
setup(function() {
|
||||
this.sinon.stub(MessageManager, 'deleteMessages').returns(
|
||||
Promise.resolve()
|
||||
);
|
||||
});
|
||||
|
||||
test('with non-empty content', function(done) {
|
||||
var handlingPromise = SystemMessageHandler.onSmsReceivedSystemMessage(
|
||||
MockMessages.sms({ messageClass: 'class-0' })
|
||||
);
|
||||
|
||||
assertWakeLockIsRequested();
|
||||
|
||||
handlingPromise.then(() => {
|
||||
sinon.assert.calledOnce(appStub.launch);
|
||||
|
||||
assertUserIsNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
Utils.alert, { raw: 'body' }, { raw: 'sender' }
|
||||
);
|
||||
|
||||
sinon.assert.notCalled(NotificationHelper.send);
|
||||
|
||||
assertWakeLockIsReleased();
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('with empty content', function(done) {
|
||||
var handlingPromise = SystemMessageHandler.onSmsReceivedSystemMessage(
|
||||
MockMessages.sms({ body: null, messageClass: 'class-0' })
|
||||
);
|
||||
|
||||
assertWakeLockIsRequested();
|
||||
|
||||
handlingPromise.then(() => {
|
||||
sinon.assert.calledOnce(appStub.launch);
|
||||
|
||||
assertUserIsNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
Utils.alert, { raw: '' }, { raw: 'sender' }
|
||||
);
|
||||
|
||||
sinon.assert.notCalled(NotificationHelper.send);
|
||||
|
||||
assertWakeLockIsReleased();
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('received class-x message', function() {
|
||||
var smsMessage, mmsMessage;
|
||||
|
||||
setup(function() {
|
||||
smsMessage = MockMessages.sms();
|
||||
mmsMessage = MockMessages.mms();
|
||||
});
|
||||
|
||||
test('when app is visible and same conversation is opened',
|
||||
function(done) {
|
||||
Navigation.isCurrentPanel.withArgs(
|
||||
'thread', { id: smsMessage.threadId }
|
||||
).returns(true);
|
||||
|
||||
var handlingPromise = SystemMessageHandler.onSmsReceivedSystemMessage(
|
||||
smsMessage
|
||||
);
|
||||
|
||||
assertWakeLockIsRequested();
|
||||
|
||||
handlingPromise.then(() => {
|
||||
assertUserIsNotified();
|
||||
|
||||
sinon.assert.notCalled(NotificationHelper.send);
|
||||
|
||||
assertWakeLockIsReleased();
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('when app is hidden and same conversation is opened',
|
||||
function(done) {
|
||||
isDocumentHidden = true;
|
||||
|
||||
Navigation.isCurrentPanel.withArgs(
|
||||
'thread', { id: smsMessage.threadId }
|
||||
).returns(true);
|
||||
|
||||
var handlingPromise = SystemMessageHandler.onSmsReceivedSystemMessage(
|
||||
smsMessage
|
||||
);
|
||||
|
||||
assertWakeLockIsRequested();
|
||||
|
||||
handlingPromise.then(() => {
|
||||
assertUserIsNotNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: smsMessage.sender },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: smsMessage.body },
|
||||
tag: 'threadId:' + smsMessage.threadId,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
|
||||
assertWakeLockIsReleased();
|
||||
|
||||
// Notification should not be closed until document is visible;
|
||||
sinon.assert.notCalled(notificationStub.close);
|
||||
|
||||
isDocumentHidden = false;
|
||||
document.addEventListener.withArgs('visibilitychange').yield();
|
||||
}).then(() => {
|
||||
sinon.assert.calledOnce(notificationStub.close);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
suite('received SMS', function() {
|
||||
test('Use sender as notification title if there is no contact',
|
||||
function(done) {
|
||||
var handlingPromise = SystemMessageHandler.onSmsReceivedSystemMessage(
|
||||
smsMessage
|
||||
);
|
||||
|
||||
assertWakeLockIsRequested();
|
||||
|
||||
handlingPromise.then(() => {
|
||||
assertUserIsNotNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: smsMessage.sender },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: smsMessage.body },
|
||||
tag: 'threadId:' + smsMessage.threadId,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
|
||||
assertWakeLockIsReleased();
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('Use sender as notification title if contact does not have name',
|
||||
function(done) {
|
||||
var contactWithoutName = new MockContact();
|
||||
contactWithoutName.name = [];
|
||||
|
||||
Contacts.findByAddress.withArgs(smsMessage.sender).returns(
|
||||
Promise.resolve([contactWithoutName])
|
||||
);
|
||||
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(smsMessage).then(
|
||||
() => {
|
||||
assertUserIsNotNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: smsMessage.sender },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: smsMessage.body },
|
||||
tag: 'threadId:' + smsMessage.threadId,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
|
||||
test('Use contact name as notification title if there is a contact',
|
||||
function(done) {
|
||||
Contacts.findByAddress.withArgs(smsMessage.sender).returns(
|
||||
Promise.resolve(MockContact.list())
|
||||
);
|
||||
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(smsMessage).then(
|
||||
() => {
|
||||
assertUserIsNotNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: 'Pepito O\'Hare' },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: smsMessage.body },
|
||||
tag: 'threadId:' + smsMessage.threadId,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
|
||||
test('Use empty string as notification body if message lacks one',
|
||||
function(done) {
|
||||
smsMessage.body = null;
|
||||
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(smsMessage).then(
|
||||
() => {
|
||||
assertUserIsNotNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: smsMessage.sender },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: '' },
|
||||
tag: 'threadId:' + smsMessage.threadId,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('received MMS', function() {
|
||||
test('if message pending on server', function(done) {
|
||||
mmsMessage.deliveryInfo[0].deliveryStatus = 'pending';
|
||||
|
||||
var handlingPromise = SystemMessageHandler.onSmsReceivedSystemMessage(
|
||||
mmsMessage
|
||||
);
|
||||
|
||||
assertWakeLockIsRequested();
|
||||
|
||||
handlingPromise.then(() => {
|
||||
assertUserIsNotNotified();
|
||||
|
||||
sinon.assert.notCalled(NotificationHelper.send);
|
||||
|
||||
assertWakeLockIsReleased();
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('if message should be manually downloaded', function(done) {
|
||||
mmsMessage.deliveryInfo[0].deliveryStatus = 'manual';
|
||||
|
||||
var handlingPromise = SystemMessageHandler.onSmsReceivedSystemMessage(
|
||||
mmsMessage
|
||||
);
|
||||
|
||||
assertWakeLockIsRequested();
|
||||
|
||||
handlingPromise.then(() => {
|
||||
assertUserIsNotNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: mmsMessage.sender },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: 'notDownloaded-title',
|
||||
tag: 'threadId:' + mmsMessage.threadId,
|
||||
data: { id: mmsMessage.id, threadId: mmsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
|
||||
assertWakeLockIsReleased();
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('if message has subject', function(done) {
|
||||
mmsMessage.subject = 'message-subject';
|
||||
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(mmsMessage).then(
|
||||
() => {
|
||||
assertUserIsNotNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: mmsMessage.sender },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: 'message-subject' },
|
||||
tag: 'threadId:' + mmsMessage.threadId,
|
||||
data: { id: mmsMessage.id, threadId: mmsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
|
||||
test('if message does not have subject, but has text slide',
|
||||
function(done) {
|
||||
this.sinon.stub(SMIL, 'parse').withArgs(mmsMessage).returns(
|
||||
Promise.resolve([{ text: 'some text' }])
|
||||
);
|
||||
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(mmsMessage).then(
|
||||
() => {
|
||||
assertUserIsNotNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: mmsMessage.sender },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: 'some text' },
|
||||
tag: 'threadId:' + mmsMessage.threadId,
|
||||
data: { id: mmsMessage.id, threadId: mmsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
|
||||
test('if message does not have subject, nor text slide',
|
||||
function(done) {
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(mmsMessage).then(
|
||||
() => {
|
||||
assertUserIsNotNotified();
|
||||
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: mmsMessage.sender },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: 'mms-message',
|
||||
tag: 'threadId:' + mmsMessage.threadId,
|
||||
data: { id: mmsMessage.id, threadId: mmsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('handling notification click', function() {
|
||||
var notificationClickHandler;
|
||||
|
||||
setup(function(done) {
|
||||
this.sinon.stub(MessageManager, 'getMessage').withArgs(
|
||||
smsMessage.id
|
||||
).returns(Promise.resolve(smsMessage));
|
||||
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(smsMessage).then(
|
||||
() => {
|
||||
// We don't want to use "yield" here because we'd like to have
|
||||
// promise-based result of yielded function.
|
||||
notificationClickHandler = notificationStub.addEventListener.
|
||||
withArgs('click').lastCall.args[1];
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
|
||||
test('if appropriate conversation is already opened', function(done) {
|
||||
Navigation.isCurrentPanel.withArgs(
|
||||
'thread', { id: smsMessage.threadId }
|
||||
).returns(true);
|
||||
|
||||
notificationClickHandler().then(() => {
|
||||
sinon.assert.called(appStub.launch);
|
||||
|
||||
sinon.assert.notCalled(Navigation.toPanel);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('if appropriate conversation is not opened', function(done) {
|
||||
notificationClickHandler().then(() => {
|
||||
sinon.assert.called(appStub.launch);
|
||||
|
||||
sinon.assert.calledWith(
|
||||
Navigation.toPanel, 'thread', { id: smsMessage.threadId }
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('if message has been deleted', function(done) {
|
||||
MessageManager.getMessage.withArgs(smsMessage.id).returns(
|
||||
Promise.reject('deleted')
|
||||
);
|
||||
|
||||
notificationClickHandler().then(() => {
|
||||
sinon.assert.called(appStub.launch);
|
||||
sinon.assert.calledWith(Utils.alert, 'deleted-sms');
|
||||
|
||||
sinon.assert.notCalled(Navigation.toPanel);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
|
||||
suite('dual SIM behavior', function() {
|
||||
setup(function() {
|
||||
smsMessage.iccId = '200';
|
||||
|
||||
this.sinon.stub(Settings, 'hasSeveralSim').returns(true);
|
||||
this.sinon.stub(Utils, 'getSimNameByIccId').withArgs('200').returns(
|
||||
Promise.resolve('SIM 2')
|
||||
);
|
||||
});
|
||||
|
||||
test('message has iccId, but only one SIM is presented',
|
||||
function(done) {
|
||||
Settings.hasSeveralSim.returns(false);
|
||||
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(smsMessage).then(
|
||||
() => {
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: smsMessage.sender },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: smsMessage.body },
|
||||
tag: 'threadId:' + smsMessage.threadId,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
|
||||
test('message iccId is unknown and several SIMs are presented',
|
||||
function(done) {
|
||||
smsMessage.iccId = null;
|
||||
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(smsMessage).then(
|
||||
() => {
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{ raw: smsMessage.sender },
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: smsMessage.body },
|
||||
tag: 'threadId:' + smsMessage.threadId,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
|
||||
test('message has iccId and several SIMs are presented',
|
||||
function(done) {
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(smsMessage).then(
|
||||
() => {
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{
|
||||
id: 'dsds-notification-title-with-sim',
|
||||
args: { sim: 'SIM 2', sender: smsMessage.sender }
|
||||
},
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: smsMessage.body },
|
||||
tag: 'threadId:' + smsMessage.threadId,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
|
||||
test('message from contact with iccId and several SIMs are presented',
|
||||
function(done) {
|
||||
Contacts.findByAddress.withArgs(smsMessage.sender).returns(
|
||||
Promise.resolve(MockContact.list())
|
||||
);
|
||||
|
||||
SystemMessageHandler.onSmsReceivedSystemMessage(smsMessage).then(
|
||||
() => {
|
||||
sinon.assert.calledWith(
|
||||
NotificationHelper.send,
|
||||
{
|
||||
id: 'dsds-notification-title-with-sim',
|
||||
args: { sim: 'SIM 2', sender: 'Pepito O\'Hare' }
|
||||
},
|
||||
{
|
||||
icon: 'iconURI',
|
||||
bodyL10n: { raw: smsMessage.body },
|
||||
tag: 'threadId:' + smsMessage.threadId,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId },
|
||||
closeOnClick: false
|
||||
}
|
||||
);
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('notification system message', function() {
|
||||
var appStub, smsMessage;
|
||||
|
||||
setup(function() {
|
||||
smsMessage = MockMessages.sms();
|
||||
appStub = sinon.stub({ launch() {} });
|
||||
|
||||
this.sinon.stub(navigator.mozApps, 'getSelf').returns(
|
||||
Promise.resolve(appStub)
|
||||
);
|
||||
|
||||
this.sinon.stub(Navigation, 'init');
|
||||
this.sinon.stub(Navigation, 'isCurrentPanel').returns(false);
|
||||
this.sinon.stub(Navigation, 'toPanel').returns(Promise.resolve());
|
||||
this.sinon.stub(Navigation, 'hasPendingInit').returns(false);
|
||||
|
||||
this.sinon.stub(MessageManager, 'getMessage').withArgs(
|
||||
smsMessage.id
|
||||
).returns(Promise.resolve(smsMessage));
|
||||
});
|
||||
|
||||
test('nothing is done when notification is removed', function(done) {
|
||||
SystemMessageHandler.onNotificationSystemMessage({
|
||||
// When notification is removed "clicked" property is "false".
|
||||
clicked: false,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId }
|
||||
}).then(() => {
|
||||
sinon.assert.notCalled(appStub.launch);
|
||||
|
||||
sinon.assert.notCalled(Navigation.toPanel);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('close the app when notification is removed with pending navigation',
|
||||
function(done) {
|
||||
|
||||
Navigation.hasPendingInit.returns(true);
|
||||
this.sinon.stub(window, 'close');
|
||||
|
||||
SystemMessageHandler.onNotificationSystemMessage({
|
||||
// When notification is removed "clicked" property is "false".
|
||||
clicked: false,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId }
|
||||
}).then(
|
||||
() => new Error('Should not enter resolved case'),
|
||||
(error) => {
|
||||
sinon.assert.called(window.close);
|
||||
assert.equal(error.message, 'Notification has been dismissed.');
|
||||
}
|
||||
).then(done, done);
|
||||
});
|
||||
|
||||
test('if appropriate conversation is already opened', function(done) {
|
||||
Navigation.isCurrentPanel.withArgs(
|
||||
'thread', { id: smsMessage.threadId }
|
||||
).returns(true);
|
||||
|
||||
SystemMessageHandler.onNotificationSystemMessage({
|
||||
clicked: true,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId }
|
||||
}).then(() => {
|
||||
sinon.assert.called(appStub.launch);
|
||||
|
||||
sinon.assert.notCalled(Navigation.toPanel);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('if appropriate conversation is not opened', function(done) {
|
||||
SystemMessageHandler.onNotificationSystemMessage({
|
||||
clicked: true,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId }
|
||||
}).then(() => {
|
||||
sinon.assert.called(appStub.launch);
|
||||
|
||||
sinon.assert.calledWith(
|
||||
Navigation.toPanel, 'thread', { id: smsMessage.threadId }
|
||||
);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('if message has been deleted', function(done) {
|
||||
MessageManager.getMessage.withArgs(smsMessage.id).returns(
|
||||
Promise.reject('deleted')
|
||||
);
|
||||
|
||||
SystemMessageHandler.onNotificationSystemMessage({
|
||||
clicked: true,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId }
|
||||
}).then(() => {
|
||||
sinon.assert.called(appStub.launch);
|
||||
sinon.assert.calledWith(Utils.alert, 'deleted-sms');
|
||||
|
||||
sinon.assert.notCalled(Navigation.toPanel);
|
||||
}).then(done, done);
|
||||
});
|
||||
|
||||
test('if message has been deleted with pending navigation', function(done) {
|
||||
MessageManager.getMessage.withArgs(smsMessage.id).returns(
|
||||
Promise.reject('deleted')
|
||||
);
|
||||
|
||||
Navigation.hasPendingInit.returns(true);
|
||||
|
||||
SystemMessageHandler.onNotificationSystemMessage({
|
||||
clicked: true,
|
||||
data: { id: smsMessage.id, threadId: smsMessage.threadId }
|
||||
}).then(() => {
|
||||
sinon.assert.called(appStub.launch);
|
||||
sinon.assert.calledWith(Utils.alert, 'deleted-sms');
|
||||
|
||||
sinon.assert.notCalled(Navigation.toPanel);
|
||||
sinon.assert.called(Navigation.init);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
41
gaia/apps/sms/views/shared/test/unit/task_runner_test.js
Executable file
41
gaia/apps/sms/views/shared/test/unit/task_runner_test.js
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
/* global TaskRunner, Promise */
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/js/task_runner.js');
|
||||
|
||||
suite('SMS App Unit-Test', function() {
|
||||
|
||||
test('runs operations serially', function(done) {
|
||||
|
||||
var taskQueue = new TaskRunner(),
|
||||
resolvers = [],
|
||||
promises = [],
|
||||
results = [];
|
||||
|
||||
promises.push(new Promise(function(resolve, reject) {
|
||||
resolvers.push(resolve);
|
||||
}));
|
||||
promises.push(new Promise(function(resolve, reject) {
|
||||
resolvers.push(resolve);
|
||||
}));
|
||||
promises.push(new Promise(function(resolve, reject) {
|
||||
resolvers.push(resolve);
|
||||
}));
|
||||
|
||||
promises.forEach(function(promise, i) {
|
||||
taskQueue.push(function() {
|
||||
results.push(i);
|
||||
return promise;
|
||||
});
|
||||
});
|
||||
|
||||
resolvers[1]();
|
||||
resolvers[0]();
|
||||
resolvers[2]();
|
||||
taskQueue.push(function() {
|
||||
Promise.resolve().then(function(resolve, reject) {
|
||||
assert.deepEqual(results, [0, 1, 2]);
|
||||
}).then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
94
gaia/apps/sms/views/shared/test/unit/thread_list_mockup.js
Executable file
94
gaia/apps/sms/views/shared/test/unit/thread_list_mockup.js
Executable file
|
|
@ -0,0 +1,94 @@
|
|||
/*global getMockupedDate */
|
||||
/*exported MockThreadList */
|
||||
|
||||
'use strict';
|
||||
|
||||
function MockThreadList(options) {
|
||||
// These Threads are not sorted in order to check that rendering
|
||||
// is working as expected, adding each thread to the right container.
|
||||
var threadsMockup = [
|
||||
{
|
||||
id: 1,
|
||||
participants: ['1977'],
|
||||
lastMessageType: 'sms',
|
||||
body: 'Alo, how are you today, my friend? :)',
|
||||
timestamp: +getMockupedDate(0),
|
||||
unreadCount: 0
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
participants: ['436797'],
|
||||
lastMessageType: 'sms',
|
||||
body: 'Sending :)',
|
||||
timestamp: +getMockupedDate(2),
|
||||
unreadCount: 0
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
participants: ['197746797'],
|
||||
lastMessageType: 'sms',
|
||||
body: 'Recibido!',
|
||||
timestamp: +getMockupedDate(1),
|
||||
unreadCount: 0
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
participants: ['1977436979'],
|
||||
lastMessageType: 'mms',
|
||||
body: 'Nothing :)',
|
||||
timestamp: +getMockupedDate(2),
|
||||
unreadCount: 2
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
participants: ['999', '888', '777'],
|
||||
lastMessageType: 'sms',
|
||||
body: '...',
|
||||
timestamp: +getMockupedDate(3),
|
||||
unreadCount: 0
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
participants: ['1977'],
|
||||
lastMessageType: 'sms',
|
||||
body: 'Alo, how are you today, my friend? :)',
|
||||
timestamp: +getMockupedDate(0),
|
||||
unreadCount: 0
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
participants: ['436797'],
|
||||
lastMessageType: 'sms',
|
||||
body: 'Sending :)',
|
||||
timestamp: +getMockupedDate(2),
|
||||
unreadCount: 0
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
participants: ['197746797'],
|
||||
lastMessageType: 'sms',
|
||||
body: 'Recibido!',
|
||||
timestamp: +getMockupedDate(1),
|
||||
unreadCount: 0
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
participants: ['1977436979'],
|
||||
lastMessageType: 'mms',
|
||||
body: 'Nothing :)',
|
||||
timestamp: +getMockupedDate(2),
|
||||
unreadCount: 2
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
participants: ['999', '888', '777'],
|
||||
lastMessageType: 'sms',
|
||||
body: '...',
|
||||
timestamp: +getMockupedDate(3),
|
||||
unreadCount: 0
|
||||
}
|
||||
];
|
||||
|
||||
return options && options.fullList ?
|
||||
threadsMockup : threadsMockup.slice(0, 5);
|
||||
}
|
||||
434
gaia/apps/sms/views/shared/test/unit/time_headers_test.js
Executable file
434
gaia/apps/sms/views/shared/test/unit/time_headers_test.js
Executable file
|
|
@ -0,0 +1,434 @@
|
|||
/*global TimeHeaders, MockL10n */
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/shared/test/unit/mocks/mock_l20n.js');
|
||||
|
||||
require('/views/shared/js/utils.js');
|
||||
require('/views/shared/js/time_headers.js');
|
||||
|
||||
suite('TimeHeaders > ', function() {
|
||||
|
||||
var realMozL10n;
|
||||
|
||||
suiteSetup(function() {
|
||||
realMozL10n = document.l10n;
|
||||
document.l10n = MockL10n;
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
document.l10n = realMozL10n;
|
||||
realMozL10n = null;
|
||||
});
|
||||
|
||||
suite('TimeHeaders.updateAll', function() {
|
||||
var existingHeaders, existingTimes;
|
||||
|
||||
setup(function() {
|
||||
this.sinon.useFakeTimers(Date.parse('2013-01-01'));
|
||||
this.sinon.spy(TimeHeaders, 'update');
|
||||
|
||||
var additionalDataset = [
|
||||
'data-date-only="true"',
|
||||
'data-hour-only="true"',
|
||||
''
|
||||
];
|
||||
|
||||
var mockThreadListMarkup = '';
|
||||
|
||||
additionalDataset.forEach(function(dataset, i) {
|
||||
var mockDate = Date.now();
|
||||
dataset += ' data-time-update="repeat"' +
|
||||
' data-time="' + mockDate + '"';
|
||||
|
||||
mockThreadListMarkup +=
|
||||
'<header ' + dataset + '>header ' + i + '</header>' +
|
||||
'<ul>' +
|
||||
'<li id="thread-1" data-time="' + mockDate + '">' +
|
||||
'<p><time data-time-update="true" ' +
|
||||
'data-time-only="true" data-time="' + mockDate +
|
||||
'"></time></p>' +
|
||||
'</li>' +
|
||||
'<li id="thread-2" data-time="' + mockDate + '">' +
|
||||
'<p><time data-time-update="true" ' +
|
||||
'data-time-only="true" data-time="' + mockDate +
|
||||
'"></time></p>' +
|
||||
'</li>' +
|
||||
'</ul>';
|
||||
});
|
||||
|
||||
document.body.innerHTML = mockThreadListMarkup;
|
||||
|
||||
TimeHeaders.updateAll();
|
||||
|
||||
existingHeaders = [];
|
||||
existingTimes = [];
|
||||
|
||||
var headers = document.querySelectorAll('header');
|
||||
for (var i = 0; i < headers.length; i++) {
|
||||
existingHeaders[i] = document.l10n.getAttributes(headers[i]);
|
||||
}
|
||||
|
||||
var timeElements = document.querySelectorAll('time');
|
||||
for (var j = 0; j < timeElements.length; j++) {
|
||||
existingTimes[j] = timeElements[j].textContent;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
suite('timezone was not changed', function() {
|
||||
test('calling at same time should not update headers ' +
|
||||
'and time elements', function() {
|
||||
var i = 0, l = 0;
|
||||
|
||||
var timeElements = document.querySelectorAll('time');
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
timeElements[i].dataset.time = Date.now();
|
||||
}
|
||||
|
||||
TimeHeaders.updateAll('header[data-time-update]');
|
||||
|
||||
var headers = document.querySelectorAll('header');
|
||||
for (i = 0, l = headers.length; i < l; i++) {
|
||||
assert.deepEqual(
|
||||
document.l10n.getAttributes(headers[i]),
|
||||
existingHeaders[i]
|
||||
);
|
||||
}
|
||||
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
assert.equal(timeElements[i].textContent, existingTimes[i]);
|
||||
}
|
||||
});
|
||||
|
||||
test('calling after one hour should not update time headers ' +
|
||||
'and time elements', function() {
|
||||
var i = 0, l = 0;
|
||||
|
||||
this.sinon.clock.tick(60 * 60 * 1000);
|
||||
|
||||
var timeElements = document.querySelectorAll('time');
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
timeElements[i].dataset.time = Date.now();
|
||||
}
|
||||
|
||||
TimeHeaders.updateAll('header[data-time-update]');
|
||||
|
||||
var headers = document.querySelectorAll('header');
|
||||
for (i = 0, l = headers.length; i < l; i++) {
|
||||
assert.deepEqual(
|
||||
document.l10n.getAttributes(headers[i]),
|
||||
existingHeaders[i]
|
||||
);
|
||||
}
|
||||
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
assert.equal(timeElements[i].textContent, existingTimes[i]);
|
||||
}
|
||||
});
|
||||
|
||||
test('calling after one day should update all date headers', function() {
|
||||
var i = 0, l = 0;
|
||||
|
||||
this.sinon.clock.tick(24 * 60 * 60 * 1000);
|
||||
|
||||
var timeElements = document.querySelectorAll('time');
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
timeElements[i].dataset.time = Date.now();
|
||||
}
|
||||
|
||||
TimeHeaders.updateAll('header[data-time-update]');
|
||||
|
||||
var headers = document.querySelectorAll('header');
|
||||
for (i = 0, l = headers.length; i < l; i++) {
|
||||
assert.notDeepEqual(
|
||||
document.l10n.getAttributes(headers[i]),
|
||||
existingHeaders[i]
|
||||
);
|
||||
}
|
||||
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
assert.equal(timeElements[i].textContent, existingTimes[i]);
|
||||
}
|
||||
|
||||
//should call update total 12 times
|
||||
//setup:3(headers)+6(times), test:3(headers)
|
||||
assert.equal(TimeHeaders.update.callCount, 12);
|
||||
});
|
||||
});
|
||||
|
||||
suite('timezone was changed', function() {
|
||||
//When timezones was changed, 'visibilitychange'.
|
||||
//Therefore 'TimeHeaders.updateAll()' is called by argument 'false'.
|
||||
test('calling at same time should not update headers ' +
|
||||
'and time elements', function() {
|
||||
var i = 0, l = 0;
|
||||
|
||||
var timeElements = document.querySelectorAll('time');
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
timeElements[i].dataset.time = Date.now();
|
||||
}
|
||||
|
||||
TimeHeaders.updateAll();
|
||||
|
||||
var headers = document.querySelectorAll('header');
|
||||
for (i = 0, l = headers.length; i < l; i++) {
|
||||
assert.deepEqual(
|
||||
document.l10n.getAttributes(headers[i]),
|
||||
existingHeaders[i]
|
||||
);
|
||||
}
|
||||
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
assert.equal(timeElements[i].textContent, existingTimes[i]);
|
||||
}
|
||||
});
|
||||
|
||||
test('calling after one hour should update all time elements',
|
||||
function() {
|
||||
var i = 0, l = 0;
|
||||
|
||||
//since we can't change timezones easily,
|
||||
//we simulate this by changing the stored 'time' dataset
|
||||
this.sinon.clock.tick(60 * 60 * 1000);
|
||||
|
||||
var timeElements = document.querySelectorAll('time');
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
timeElements[i].dataset.time = Date.now();
|
||||
}
|
||||
|
||||
TimeHeaders.updateAll();
|
||||
|
||||
var headers = document.querySelectorAll('header');
|
||||
for (i = 0, l = headers.length; i < l; i++) {
|
||||
assert.deepEqual(
|
||||
document.l10n.getAttributes(headers[i]),
|
||||
existingHeaders[i]
|
||||
);
|
||||
}
|
||||
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
assert.notEqual(timeElements[i].textContent, existingTimes[i]);
|
||||
}
|
||||
});
|
||||
|
||||
test('calling after one day should update all date headers ' +
|
||||
'and time elements', function() {
|
||||
var i = 0, l = 0;
|
||||
|
||||
//since we can't change timezones easily,
|
||||
//we simulate this by changing the stored 'time' dataset
|
||||
this.sinon.clock.tick(24 * 60 * 60 * 1000);
|
||||
|
||||
var timeElements = document.querySelectorAll('time');
|
||||
for (i = 0, l = timeElements.length; i < l; i++) {
|
||||
timeElements[i].dataset.time = Date.now();
|
||||
}
|
||||
|
||||
TimeHeaders.updateAll();
|
||||
|
||||
var headers = document.querySelectorAll('header');
|
||||
for (i = 0, l = headers.length; i < l; i++) {
|
||||
assert.notDeepEqual(
|
||||
document.l10n.getAttributes(headers[i]),
|
||||
existingHeaders[i]
|
||||
);
|
||||
}
|
||||
|
||||
//should call update total 18 times
|
||||
//setup:3(headers)+6(times), test:3(headers)+6(times)
|
||||
assert.equal(TimeHeaders.update.callCount, 18);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('TimeHeaders.update', function() {
|
||||
var subject, time;
|
||||
|
||||
setup(function() {
|
||||
subject = document.createElement('header');
|
||||
subject.dataset.timeUpdate = 'repeat';
|
||||
time = Date.parse('2013-01-01');
|
||||
subject.dataset.time = time;
|
||||
});
|
||||
|
||||
test('date and time header', function() {
|
||||
TimeHeaders.update(subject);
|
||||
|
||||
var formatter = new Intl.DateTimeFormat(navigator.languages, {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
year: 'numeric',
|
||||
hour12: navigator.mozHour12,
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
});
|
||||
|
||||
var content = subject.textContent;
|
||||
assert.equal(content, formatter.format(time));
|
||||
});
|
||||
|
||||
test('date header', function() {
|
||||
subject.dataset.dateOnly = 'true';
|
||||
TimeHeaders.update(subject);
|
||||
|
||||
var formatter = new Intl.DateTimeFormat(navigator.languages, {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
year: 'numeric',
|
||||
});
|
||||
|
||||
var content = subject.textContent;
|
||||
assert.equal(content, formatter.format(time),
|
||||
'subject.textContent should not contain formattedTime');
|
||||
});
|
||||
|
||||
test('time header', function() {
|
||||
subject.dataset.timeOnly = 'true';
|
||||
TimeHeaders.update(subject);
|
||||
|
||||
var formatter = new Intl.DateTimeFormat(navigator.languages, {
|
||||
hour12: navigator.mozHour12,
|
||||
hour: 'numeric',
|
||||
minute: 'numeric'
|
||||
});
|
||||
|
||||
var content = subject.textContent;
|
||||
assert.equal(
|
||||
content, formatter.format(time),
|
||||
'subject.textContent should not contain formattedDate'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('TimeHeaders.startScheduler', function() {
|
||||
|
||||
setup(function() {
|
||||
this.callTimes = [];
|
||||
this.sinon.useFakeTimers();
|
||||
this.updateStub = this.sinon.stub(TimeHeaders, 'updateAll',
|
||||
function() {
|
||||
this.callTimes.push(Date.now());
|
||||
}.bind(this));
|
||||
});
|
||||
|
||||
test('timeout on minute boundary', function() {
|
||||
// "Fri Jul 12 2013 16:01:54 GMT-0400 (EDT)"
|
||||
var start = 1373659314572;
|
||||
|
||||
this.sinon.clock.tick(start);
|
||||
TimeHeaders.startScheduler();
|
||||
this.sinon.clock.tick(100 * 1000);
|
||||
|
||||
// Fri Jul 12 2013 16:02:00 GMT-0400 (EDT)
|
||||
assert.equal(this.callTimes[0], 1373659320000);
|
||||
// Fri Jul 12 2013 16:03:00 GMT-0400 (EDT)
|
||||
assert.equal(this.callTimes[1], 1373659380000);
|
||||
});
|
||||
|
||||
test('multiple calls converge', function() {
|
||||
this.updateStub.reset();
|
||||
|
||||
for (var i = 0; i < 100; i++) {
|
||||
TimeHeaders.startScheduler();
|
||||
}
|
||||
this.sinon.clock.tick(60 * 1000);
|
||||
assert.equal(this.updateStub.callCount, 1);
|
||||
});
|
||||
});
|
||||
|
||||
suite('TimeHeaders.stopScheduler', function() {
|
||||
setup(function() {
|
||||
this.sinon.spy(window, 'clearTimeout');
|
||||
TimeHeaders.stopScheduler();
|
||||
});
|
||||
|
||||
test('stopScheduler called', function() {
|
||||
assert.ok(window.clearTimeout.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('TimeHeaders.init', function() {
|
||||
var isDocumentHidden;
|
||||
|
||||
suiteSetup(function() {
|
||||
Object.defineProperty(document, 'hidden', {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
return isDocumentHidden;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
suiteTeardown(function() {
|
||||
delete document.hidden;
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
this.sinon.spy(TimeHeaders, 'startScheduler');
|
||||
this.sinon.spy(TimeHeaders, 'stopScheduler');
|
||||
this.sinon.spy(TimeHeaders, 'updateAll');
|
||||
});
|
||||
|
||||
suite('init itself', function() {
|
||||
setup(function() {
|
||||
isDocumentHidden = false;
|
||||
|
||||
this.sinon.spy(TimeHeaders, 'init');
|
||||
TimeHeaders.init();
|
||||
});
|
||||
|
||||
test('startScheduler is called', function() {
|
||||
// Because in startScheduler, we will still call stopScheduler,
|
||||
// that's why I use this check to make sure we call startScheduler
|
||||
// correctly.
|
||||
assert.ok(TimeHeaders.startScheduler.calledBefore(
|
||||
TimeHeaders.stopScheduler));
|
||||
assert.ok(TimeHeaders.updateAll.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('when document is hidden', function() {
|
||||
setup(function() {
|
||||
isDocumentHidden = false;
|
||||
TimeHeaders.init();
|
||||
|
||||
TimeHeaders.startScheduler.reset();
|
||||
TimeHeaders.stopScheduler.reset();
|
||||
TimeHeaders.updateAll.reset();
|
||||
|
||||
isDocumentHidden = true;
|
||||
document.dispatchEvent(new CustomEvent('visibilitychange'));
|
||||
});
|
||||
|
||||
test('TimeHeaders.stopScheduler is called', function() {
|
||||
assert.ok(TimeHeaders.stopScheduler.called);
|
||||
assert.ok(!TimeHeaders.startScheduler.called);
|
||||
assert.ok(!TimeHeaders.updateAll.called);
|
||||
});
|
||||
});
|
||||
|
||||
suite('when document is not hidden', function() {
|
||||
setup(function() {
|
||||
isDocumentHidden = true;
|
||||
TimeHeaders.init();
|
||||
|
||||
TimeHeaders.startScheduler.reset();
|
||||
TimeHeaders.stopScheduler.reset();
|
||||
|
||||
isDocumentHidden = false;
|
||||
document.dispatchEvent(new CustomEvent('visibilitychange'));
|
||||
});
|
||||
|
||||
test('TimeHeaders.startScheduler is called', function() {
|
||||
assert.ok(TimeHeaders.updateAll.called);
|
||||
assert.ok(TimeHeaders.startScheduler.calledBefore(
|
||||
TimeHeaders.stopScheduler));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
11
gaia/apps/sms/views/shared/test/unit/utils_mockup.js
Executable file
11
gaia/apps/sms/views/shared/test/unit/utils_mockup.js
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
/*exported getMockupedDate */
|
||||
|
||||
'use strict';
|
||||
|
||||
function getMockupedDate(diffDays) {
|
||||
var year = 2013;
|
||||
var month = 1;
|
||||
var day = 13;
|
||||
var hour = 1, minute = 1;
|
||||
return new Date(year, month, day - diffDays, hour, minute, 0);
|
||||
}
|
||||
1883
gaia/apps/sms/views/shared/test/unit/utils_test.js
Executable file
1883
gaia/apps/sms/views/shared/test/unit/utils_test.js
Executable file
File diff suppressed because it is too large
Load diff
87
gaia/apps/sms/views/shared/test/unit/wbmp_test.js
Executable file
87
gaia/apps/sms/views/shared/test/unit/wbmp_test.js
Executable file
|
|
@ -0,0 +1,87 @@
|
|||
/*global WBMP,
|
||||
AssetsHelper,
|
||||
Promise
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('/views/shared/js/wbmp.js');
|
||||
|
||||
suite('wbmp.js >', function() {
|
||||
|
||||
var wbmpBlob, wbmpAsPngBlob;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
var mediaFolder = '/views/shared/test/unit/media';
|
||||
var blobPromises = [
|
||||
AssetsHelper.loadFileBlob(`${mediaFolder}/grid.png`).then(
|
||||
(blob) => wbmpAsPngBlob = blob
|
||||
),
|
||||
AssetsHelper.loadFileBlob(`${mediaFolder}/grid.wbmp`).then(
|
||||
(blob) => wbmpBlob = blob
|
||||
)
|
||||
];
|
||||
|
||||
Promise.all(blobPromises).then(() => done(), done);
|
||||
});
|
||||
|
||||
function writeBlobToCanvas(blob, callback) {
|
||||
var canvas = document.createElement('canvas');
|
||||
var image = new Image();
|
||||
|
||||
var blobUrl = window.URL.createObjectURL(blob);
|
||||
image.src = blobUrl;
|
||||
image.onload = function() {
|
||||
window.URL.revokeObjectURL(this.src);
|
||||
canvas.height = image.naturalHeight;
|
||||
canvas.width = image.naturalWidth;
|
||||
var context = canvas.getContext('2d');
|
||||
context.drawImage(image, 0, 0);
|
||||
callback(canvas);
|
||||
};
|
||||
}
|
||||
|
||||
function assertPngEquals(pngBlob1, pngBlob2, done) {
|
||||
var canvases = [];
|
||||
|
||||
function canvasReady(canvas) {
|
||||
canvases.push(canvas);
|
||||
if (canvases.length === 2) {
|
||||
done(compareCanvases);
|
||||
}
|
||||
}
|
||||
|
||||
function compareCanvases() {
|
||||
var c1 = canvases[0];
|
||||
var c2 = canvases[1];
|
||||
assert.equal(c1.width, c2.width);
|
||||
assert.equal(c1.height, c2.height);
|
||||
|
||||
var id1 = c1.getContext('2d').getImageData(0, 0, c1.width, c1.height);
|
||||
var id2 = c2.getContext('2d').getImageData(0, 0, c2.width, c2.height);
|
||||
|
||||
var data1 = id1.data;
|
||||
var data2 = id2.data;
|
||||
|
||||
assert.equal(data1.length, data2.length);
|
||||
|
||||
for (var i = 0, l = data1.length; i < l; i++) {
|
||||
assert.equal(data1[i], data2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
writeBlobToCanvas(pngBlob1, canvasReady);
|
||||
writeBlobToCanvas(pngBlob2, canvasReady);
|
||||
}
|
||||
|
||||
test('Decode correctly', function(done) {
|
||||
var reader = new FileReader();
|
||||
reader.readAsArrayBuffer(wbmpBlob);
|
||||
reader.onload = function() {
|
||||
var arrayBuffer = this.result;
|
||||
WBMP.decode(arrayBuffer, function(blob) {
|
||||
assertPngEquals(blob, wbmpAsPngBlob, done);
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue