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
27
gaia/apps/email/test/build/integration/email_test.js
Executable file
27
gaia/apps/email/test/build/integration/email_test.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
/* jshint node: true, mocha: true */
|
||||
/* global suiteSetup */
|
||||
|
||||
var helper = require('helper');
|
||||
|
||||
suite('Email tests', function() {
|
||||
suiteSetup(helper.cleanupWorkspace);
|
||||
teardown(helper.cleanupWorkspace);
|
||||
|
||||
test('APP=email make', function(done) {
|
||||
helper.exec('APP=email make', function(error, stdout, stderr) {
|
||||
helper.checkError(error, stdout, stderr);
|
||||
|
||||
var zipPath = process.cwd() +
|
||||
'/profile/webapps/email.gaiamobile.org/application.zip';
|
||||
var checkList = ['manifest.webapp'];
|
||||
|
||||
checkList.forEach(function(filePath) {
|
||||
helper.checkFilePathInZip(zipPath, filePath);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
139
gaia/apps/email/test/config.js
Executable file
139
gaia/apps/email/test/config.js
Executable file
|
|
@ -0,0 +1,139 @@
|
|||
'use strict';
|
||||
/*global requirejs, define, TestUrlResolver */
|
||||
// Test config. config.js has the non-test, runtime config
|
||||
(function(global) {
|
||||
var contextIdCount = 0;
|
||||
var baseConfig = {
|
||||
baseUrl: TestUrlResolver.resolve('email/js'),
|
||||
paths: {
|
||||
test: '../test',
|
||||
l10nbase: '../shared/js/l10n',
|
||||
moz_intl: '../shared/test/unit/mocks/mock_moz_intl',
|
||||
style: '../style',
|
||||
shared: '../shared'
|
||||
},
|
||||
map: {
|
||||
'*': {
|
||||
'api': 'test/unit/mock_api'
|
||||
}
|
||||
},
|
||||
shim: {
|
||||
moz_intl: {
|
||||
deps: ['l10nbase'],
|
||||
exports: 'MockMozIntl'
|
||||
},
|
||||
'shared/js/mime_mapper': {
|
||||
exports: 'MimeMapper'
|
||||
},
|
||||
|
||||
'shared/js/notification_helper': {
|
||||
exports: 'NotificationHelper'
|
||||
},
|
||||
|
||||
'shared/js/accessibility_helper': {
|
||||
exports: 'AccessibilityHelper'
|
||||
},
|
||||
|
||||
'shared/js/gesture_detector': {
|
||||
exports: 'GestureDetector'
|
||||
}
|
||||
},
|
||||
config: {
|
||||
template: {
|
||||
tagToId: function(tag) {
|
||||
return tag.replace(/^cards-/, 'cards/')
|
||||
.replace(/^lst-/, 'cards/lst/')
|
||||
.replace(/^msg-/, 'cards/msg/')
|
||||
.replace(/^cmp-/, 'cards/cmp/')
|
||||
.replace(/-/g, '_');
|
||||
}
|
||||
},
|
||||
|
||||
element: {
|
||||
idToTag: function(id) {
|
||||
return id.toLowerCase()
|
||||
.replace(/^cards\/lst\//, 'lst-')
|
||||
.replace(/^cards\/msg\//, 'msg-')
|
||||
.replace(/^cards\/cmp\//, 'cmp-')
|
||||
.replace(/[^a-z]/g, '-');
|
||||
}
|
||||
}
|
||||
},
|
||||
definePrim: 'prim'
|
||||
};
|
||||
|
||||
global.testConfig = function(obj, ids, callback) {
|
||||
var config = obj.config,
|
||||
mocks = obj.mocks,
|
||||
defines = obj.defines,
|
||||
suiteTeardown = obj.suiteTeardown,
|
||||
done = obj.done,
|
||||
contextId = 'test' + (contextIdCount += 1);
|
||||
|
||||
var req = requirejs.config({
|
||||
context: 'test' + (contextIdCount += 1)
|
||||
});
|
||||
|
||||
req.config(baseConfig);
|
||||
if (config) {
|
||||
req.config(config);
|
||||
}
|
||||
|
||||
// Tears down the context.
|
||||
if (suiteTeardown) {
|
||||
suiteTeardown(function() {
|
||||
delete requirejs.contexts[contextId];
|
||||
});
|
||||
}
|
||||
|
||||
// Set up the mocks.
|
||||
if (mocks) {
|
||||
Object.keys(mocks).forEach(function(id) {
|
||||
// Create a map config so that modules that ask
|
||||
// for the target ID get the
|
||||
var map = {
|
||||
'*': {}
|
||||
};
|
||||
map['*'][id] = '_test/' + id;
|
||||
map['_test/' + id] = {};
|
||||
map['_test/' + id][id] = id;
|
||||
req.config(map);
|
||||
|
||||
var args = mocks[id];
|
||||
if (!Array.isArray(args)) {
|
||||
args = [args];
|
||||
}
|
||||
|
||||
define.apply(null, ['_test/' + id].concat(args));
|
||||
});
|
||||
}
|
||||
|
||||
if (defines) {
|
||||
Object.keys(defines).forEach(function(id) {
|
||||
var args = defines[id];
|
||||
if (!Array.isArray(args)) {
|
||||
args = [args];
|
||||
}
|
||||
define.apply(null, [id].concat(args));
|
||||
});
|
||||
}
|
||||
|
||||
if (ids) {
|
||||
req(ids, function() {
|
||||
if (callback) {
|
||||
callback.apply(null, Array.slice(arguments));
|
||||
}
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
}, function(err) {
|
||||
if (done) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return req;
|
||||
};
|
||||
}(window));
|
||||
|
||||
17
gaia/apps/email/test/marionette/.jshintrc
Executable file
17
gaia/apps/email/test/marionette/.jshintrc
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../../../../.jshintrc",
|
||||
"predef": [
|
||||
"suite",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"marionette",
|
||||
"marionetteScriptFinished",
|
||||
"require",
|
||||
"define",
|
||||
"requireApp",
|
||||
"sinon"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
'use strict';
|
||||
/*jshint node: true */
|
||||
/*global marionette */
|
||||
|
||||
var Email = require('./lib/email');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
marionette('activity create email account', function() {
|
||||
var app,
|
||||
client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
}),
|
||||
server = serverHelper.use({
|
||||
credentials: {
|
||||
username: 'testy1',
|
||||
password: 'testy1'
|
||||
}
|
||||
}, this);
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
|
||||
client.contentScript.inject(__dirname +
|
||||
'/lib/mocks/mock_navigator_moz_set_message_handler_activity.js');
|
||||
|
||||
app.launch();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* XXX: Bug 907013 changes the DOM structure of window.confirm,
|
||||
* and this is causing problems in finding out the confirm dialog
|
||||
* as well as tapping the ok button.
|
||||
*/
|
||||
test.skip('should complete activity after creating account', function() {
|
||||
|
||||
client.waitFor(function() {
|
||||
return client.executeScript(function() {
|
||||
var req = window.wrappedJSObject.require;
|
||||
return req.defined('cards');
|
||||
});
|
||||
});
|
||||
|
||||
client.executeScript(function() {
|
||||
window.wrappedJSObject.fireMessageHandler({
|
||||
source: {
|
||||
name: 'new',
|
||||
data: {
|
||||
type: 'mail',
|
||||
URI: 'mailto:testy@localhost'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.confirmWantAccount();
|
||||
app.manualSetupImapEmail(server, 'waitForCompose');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
81
gaia/apps/email/test/marionette/font_utils_test.js
Executable file
81
gaia/apps/email/test/marionette/font_utils_test.js
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
'use strict';
|
||||
|
||||
marionette('FontSizeUtils >', function() {
|
||||
var ReflowHelper =
|
||||
require('../../../../tests/jsmarionette/plugins/reflow_helper.js');
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var EMAIL_APP = 'app://email.gaiamobile.org';
|
||||
var client = marionette.client();
|
||||
|
||||
var reflowHelper, sys;
|
||||
|
||||
setup(function() {
|
||||
sys = client.loader.getAppClass('system');
|
||||
reflowHelper = new ReflowHelper(client);
|
||||
|
||||
var email = sys.waitForLaunch(EMAIL_APP);
|
||||
client.waitFor(function() {
|
||||
return email.displayed();
|
||||
});
|
||||
|
||||
client.apps.switchToApp(EMAIL_APP);
|
||||
});
|
||||
|
||||
test('FontSizeUtils._reformatHeaderText creates 1 reflow a header',
|
||||
function() {
|
||||
reflowHelper.startTracking(EMAIL_APP);
|
||||
var beforeCount = reflowHelper.getCount();
|
||||
|
||||
var headerCount = client.executeAsyncScript(function() {
|
||||
|
||||
// Change the text of each header to trigger resize logic.
|
||||
var reformatHeaders = function() {
|
||||
var headers = document.querySelectorAll('header > h1');
|
||||
for (var i = 0; i < headers.length; i++) {
|
||||
headers[i].textContent =
|
||||
'really large giant header that couldnt possibly fit';
|
||||
}
|
||||
marionetteScriptFinished(headers.length);
|
||||
};
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
window.addEventListener('DOMContentLoaded', reformatHeaders);
|
||||
} else {
|
||||
reformatHeaders();
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure we have at most as many reflows as there are headers.
|
||||
var reflowCount = reflowHelper.getCount() - beforeCount;
|
||||
assert(reflowCount <= headerCount, 'Performed too many reflows');
|
||||
|
||||
// Now set all the header text really small, and check reflow count;
|
||||
beforeCount = reflowHelper.getCount();
|
||||
var newHeaderCount = client.executeAsyncScript(function() {
|
||||
|
||||
// Change the text of each header to trigger resize logic.
|
||||
var reformatHeaders = function() {
|
||||
var headers = document.querySelectorAll('header > h1');
|
||||
for (var i = 0; i < headers.length; i++) {
|
||||
headers[i].textContent = 'tiny';
|
||||
}
|
||||
marionetteScriptFinished(headers.length);
|
||||
};
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
window.addEventListener('DOMContentLoaded', reformatHeaders);
|
||||
} else {
|
||||
reformatHeaders();
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(headerCount, newHeaderCount, 'header count stays the same');
|
||||
reflowCount = reflowHelper.getCount() - beforeCount;
|
||||
assert(reflowCount <= newHeaderCount, 'Performed too many reflows');
|
||||
|
||||
reflowHelper.stopTracking();
|
||||
assert(true, 'created 1 reflow per header');
|
||||
});
|
||||
});
|
||||
32
gaia/apps/email/test/marionette/lib/debug.js
Executable file
32
gaia/apps/email/test/marionette/lib/debug.js
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
/*global module, console, process */
|
||||
|
||||
// Run a test with DEBUG=true in front to get console listing of
|
||||
// helper method calls:
|
||||
// DEBUG=true ./bin/gaia-marionette whatever_test.js
|
||||
var debug = process.env.DEBUG;
|
||||
|
||||
module.exports = function(logPrefix, obj) {
|
||||
|
||||
if (!debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Optionally log all calls done to prototype methods. Uncomment this
|
||||
// section to get traces when trying to debug where flow gets stuck.
|
||||
Object.keys(obj).forEach(function(key) {
|
||||
var desc = Object.getOwnPropertyDescriptor(obj, key);
|
||||
if (!desc.get && !desc.set && typeof obj[key] === 'function') {
|
||||
var oldMethod = obj[key];
|
||||
obj[key] = function() {
|
||||
|
||||
var args = Array.prototype.slice.call(arguments, 0).map(function(arg) {
|
||||
return String(arg);
|
||||
}).join(', ');
|
||||
|
||||
console.log(logPrefix + '.' + key + '(' + args + ')');
|
||||
return oldMethod.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
768
gaia/apps/email/test/marionette/lib/email.js
Executable file
768
gaia/apps/email/test/marionette/lib/email.js
Executable file
|
|
@ -0,0 +1,768 @@
|
|||
/*jshint node: true, browser: true */
|
||||
'use strict';
|
||||
function Email(client) {
|
||||
this.client = client.scope({ searchTimeout: 20000 });
|
||||
}
|
||||
module.exports = Email;
|
||||
|
||||
Email.EMAIL_ORIGIN = 'app://email.gaiamobile.org';
|
||||
|
||||
var Selector = {
|
||||
setupNameInput: 'cards-setup-account-info .sup-info-name',
|
||||
setupEmailInput: 'cards-setup-account-info .sup-info-email',
|
||||
nextButton: 'cards-setup-account-info .sup-info-next-btn',
|
||||
prefsNextButton: 'cards-setup-account-prefs .sup-info-next-btn',
|
||||
manualSetupNameInput: '.sup-manual-form .sup-info-name',
|
||||
manualSetupEmailInput: '.sup-manual-form .sup-info-email',
|
||||
manualSetupImapPasswordInput:
|
||||
'.sup-manual-form .sup-manual-composite-password',
|
||||
manualSetupImapUsernameInput:
|
||||
'.sup-manual-form .sup-manual-composite-username',
|
||||
manualSetupImapHostnameInput:
|
||||
'.sup-manual-form .sup-manual-composite-hostname',
|
||||
manualSetupImapPortInput: '.sup-manual-form .sup-manual-composite-port',
|
||||
manualSetupImapSocket: '.sup-manual-form .sup-manual-composite-socket',
|
||||
manualSetupSmtpUsernameInput: '.sup-manual-form .sup-manual-smtp-username',
|
||||
manualSetupSmtpHostnameInput: '.sup-manual-form .sup-manual-smtp-hostname',
|
||||
manualSetupSmtpPasswordInput: '.sup-manual-form .sup-manual-smtp-password',
|
||||
manualSetupSmtpPortInput: '.sup-manual-form .sup-manual-smtp-port',
|
||||
manualSetupSmtpSocket: '.sup-manual-form .sup-manual-smtp-socket',
|
||||
manualNextButton: 'cards-setup-manual-config .sup-manual-next-btn',
|
||||
msgDownBtn: 'cards-message-reader .msg-down-btn',
|
||||
msgListScrollOuter: 'cards-message-list .msg-list-scrollouter',
|
||||
editMode: 'cards-message-list .msg-edit-btn',
|
||||
editModeCheckBoxes:
|
||||
'cards-message-list .msg-header-item[data-index="0"] label.pack-checkbox',
|
||||
editModeTrash: 'cards-message-list button.msg-delete-btn',
|
||||
msgUpBtn: 'cards-message-reader .msg-up-btn',
|
||||
msgEnvelopeSubject: 'cards-message-reader .msg-envelope-subject',
|
||||
showMailButton: 'cards-setup-done .sup-show-mail-btn',
|
||||
confirmDialogOkButton: '.confirm-dialog-form .confirm-dialog-ok',
|
||||
manualConfigButton: '.scrollregion-below-header .sup-manual-config-btn',
|
||||
composeButton: '.msg-list-header .msg-compose-btn',
|
||||
composeEmailContainer: 'cards-compose .cmp-to-container',
|
||||
composeEmailInput: 'cards-compose .cmp-addr-text',
|
||||
composeSubjectInput: 'cards-compose .cmp-subject-text',
|
||||
composeBodyInput: 'cards-compose .cmp-body-text',
|
||||
composeSendButton: 'cards-compose .cmp-send-btn',
|
||||
composeBackButton: 'cards-compose .cmp-back-btn',
|
||||
composeDraftDiscard: '#cmp-draft-discard',
|
||||
composeDraftSave: '#cmp-draft-save',
|
||||
composeErrorMessage: 'cards-compose .cmp-error-message',
|
||||
refreshButton: '.card.center .msg-refresh-btn',
|
||||
messageHeaderItem:
|
||||
'.msg-vscroll-container .msg-header-item',
|
||||
cardMessageReader: 'cards-message-reader',
|
||||
currentCardInputs: '.card.center input[type="text"]',
|
||||
replyMenuButton: '.msg-reply-btn',
|
||||
replyMenu: '.msg-reply-menu',
|
||||
replyMenuReply: '.msg-reply-menu-reply',
|
||||
replyMenuForward: '.msg-reply-menu-forward',
|
||||
replyMenuAll: '.msg-reply-menu-reply-all',
|
||||
searchTextTease: '.msg-search-text-tease',
|
||||
searchCard: 'cards-message-list-search',
|
||||
folderListButton: '.msg-list-header .msg-folder-list-btn',
|
||||
folderListCloseButton: 'cards-folder-picker .fld-header-back',
|
||||
folderListContents: 'cards-folder-picker .fld-acct-scrollinner',
|
||||
settingsButton: '.fld-nav-toolbar',
|
||||
settingsDoneButton: 'cards-settings-main [data-l10n-id="settings-done"]',
|
||||
addAccountButton: 'cards-settings-main .tng-account-add',
|
||||
accountListButton: '.fld-acct-header',
|
||||
accountListContainer: '.fld-accountlist-container',
|
||||
settingsMainAccountItems: '.tng-accounts-container .tng-account-item',
|
||||
syncIntervalSelect: '.tng-account-check-interval ',
|
||||
// Checkboxes are weird: hidden to marionette, but the associated label
|
||||
// is clickable and does the job.
|
||||
notifyEmailCheckbox: '.tng-notify-mail-label',
|
||||
accountSettingsBackButton: 'cards-settings-account .tng-back-btn',
|
||||
localDraftsItem: '.fld-folders-container a[data-type=localdrafts]',
|
||||
outboxItem: '.fld-folders-container a[data-type=outbox]',
|
||||
outboxItemSyncIcon: '.msg-header-syncing-section',
|
||||
msgLastSync: '.msg-last-synced-value',
|
||||
toaster: 'section[role="status"]'
|
||||
};
|
||||
|
||||
Email.prototype = {
|
||||
/**
|
||||
* Send some emails and then receive them.
|
||||
*
|
||||
* @param {Array} messages list of messages with to, subject, and body.
|
||||
* @param {Number} [messageSyncIndex] the index into the list of messages in
|
||||
* the message list that indicates synchronization is complete. Uses the last
|
||||
* index in messages by default.
|
||||
*/
|
||||
sendAndReceiveMessages: function(messages, messageSyncIndex) {
|
||||
if (messageSyncIndex === undefined) {
|
||||
messageSyncIndex = messages.length - 1;
|
||||
}
|
||||
|
||||
messages.forEach(function(message) {
|
||||
this.tapCompose();
|
||||
this.typeTo(message.to);
|
||||
this.typeSubject(message.subject);
|
||||
this.typeBody(message.body);
|
||||
this.tapSend();
|
||||
}.bind(this));
|
||||
|
||||
this.tapRefreshButton();
|
||||
this.waitForSynchronized(messageSyncIndex);
|
||||
},
|
||||
|
||||
waitForToaster: function() {
|
||||
var toaster = this.client.helper.waitForElement(Selector.toaster);
|
||||
this.client.helper.waitForElementToDisappear(toaster);
|
||||
},
|
||||
|
||||
get msgDownBtn() {
|
||||
return this.client.helper.waitForElement(Selector.msgDownBtn);
|
||||
},
|
||||
|
||||
get msgListScrollOuter() {
|
||||
return this.client.helper.waitForElement(Selector.msgListScrollOuter);
|
||||
},
|
||||
|
||||
get msgUpBtn() {
|
||||
return this.client.helper.waitForElement(Selector.msgUpBtn);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {boolean} up whether we're advancing up or down.
|
||||
*/
|
||||
advanceMessageReader: function(up) {
|
||||
var el = up ? this.msgUpBtn : this.msgDownBtn;
|
||||
el.click();
|
||||
this.waitForMessageReader();
|
||||
},
|
||||
|
||||
getMessageReaderSubject: function() {
|
||||
return this.client.helper
|
||||
.waitForElement(Selector.msgEnvelopeSubject)
|
||||
.text();
|
||||
},
|
||||
|
||||
getComposeTo: function() {
|
||||
var container =
|
||||
this.client.helper.waitForElement(Selector.composeEmailContainer);
|
||||
var text = container.text();
|
||||
return text;
|
||||
},
|
||||
|
||||
getComposeErrorMessage: function() {
|
||||
return this.client.helper
|
||||
.waitForElement(Selector.composeErrorMessage)
|
||||
.text();
|
||||
},
|
||||
|
||||
getLastSyncText: function() {
|
||||
return this.client.helper
|
||||
.waitForElement(Selector.msgLastSync)
|
||||
.text();
|
||||
},
|
||||
|
||||
manualSetupImapEmail: function(server, finalActionName) {
|
||||
// setup a IMAP email account
|
||||
var email = server.imap.username + '@' + server.imap.hostname;
|
||||
|
||||
// wait for the setup page is loaded
|
||||
this._setupTypeName(server.imap.username);
|
||||
this._setupTypeEmail(email);
|
||||
|
||||
this._waitForElementNoTransition(Selector.manualConfigButton).tap();
|
||||
this._waitForTransitionEnd('setup_manual_config');
|
||||
|
||||
this._manualSetupTypeImapUsername(server.imap.username);
|
||||
this._manualSetupTypeImapHostname(server.imap.hostname);
|
||||
this._manualSetupTypeImapPassword(server.imap.password);
|
||||
this._manualSetupTypeImapPort(server.imap.port);
|
||||
this._manualSetupUpdateSocket('manualSetupImapSocket');
|
||||
|
||||
this._manualSetupTypeSmtpUsername(server.smtp.username);
|
||||
this._manualSetupTypeSmtpHostname(server.smtp.hostname);
|
||||
this._manualSetupTypeSmtpPassword(server.smtp.password);
|
||||
this._manualSetupTypeSmtpPort(server.smtp.port);
|
||||
this._manualSetupUpdateSocket('manualSetupSmtpSocket');
|
||||
|
||||
this._finishSetup(Selector.manualNextButton, finalActionName);
|
||||
},
|
||||
|
||||
_finishSetup: function(nextSelector, finalActionName) {
|
||||
this._tapNext(nextSelector);
|
||||
this._waitForElementNoTransition(Selector.prefsNextButton);
|
||||
this._tapNext(Selector.prefsNextButton, 'setup_done');
|
||||
|
||||
this._waitForElementNoTransition(Selector.showMailButton);
|
||||
this.client.helper
|
||||
.waitForElement(Selector.showMailButton)
|
||||
.tap();
|
||||
return this[finalActionName || 'waitForMessageList']();
|
||||
},
|
||||
|
||||
// Waits for confirm dialog to show up, then clicks OK to confirm
|
||||
// going to setting up a new account after triggering email launch
|
||||
// from an activity.
|
||||
confirmWantAccount: function() {
|
||||
this.client.switchToFrame();
|
||||
this.client.apps.switchToApp(Email.EMAIL_ORIGIN);
|
||||
this.client.helper.waitForElement(Selector.confirmDialogOkButton).tap();
|
||||
this.client.helper.waitForElement(Selector.manualConfigButton);
|
||||
this.client.helper.waitForElement(Selector.manualConfigButton).tap();
|
||||
},
|
||||
|
||||
tapFolderListButton: function() {
|
||||
this._tapSelector(Selector.folderListButton);
|
||||
this._waitForElementNoTransition(Selector.settingsButton);
|
||||
this._waitForTransitionEnd('folder_picker');
|
||||
this.client.helper.waitForElement(Selector.folderListContents);
|
||||
},
|
||||
|
||||
tapFolderListCloseButton: function() {
|
||||
this._tapSelector(Selector.folderListCloseButton);
|
||||
this.waitForMessageList();
|
||||
},
|
||||
|
||||
tapAccountListButton: function() {
|
||||
// XXX: Workaround util http://bugzil.la/912873 is fixed.
|
||||
// Wait for 500ms to let the element be clickable
|
||||
this.client.helper.wait(500);
|
||||
|
||||
this.client.helper.waitForElement(Selector.accountListButton).tap();
|
||||
this.client.helper.waitForElement(Selector.accountListContainer);
|
||||
},
|
||||
|
||||
tapLocalDraftsItem: function() {
|
||||
// we should already be looking at the folder list, no need to wait.
|
||||
this._waitForElementNoTransition(Selector.localDraftsItem).tap();
|
||||
// clicking that transitions us back to the message list; wait for us
|
||||
// to get there.
|
||||
this._waitForTransitionEnd('message_list');
|
||||
},
|
||||
|
||||
tapOutboxItem: function() {
|
||||
this._waitForElementNoTransition(Selector.outboxItem).tap();
|
||||
this._waitForTransitionEnd('message_list');
|
||||
},
|
||||
|
||||
getOutboxItemSyncIconForIndex: function(index) {
|
||||
var header = this.getHeaderAtIndex(index);
|
||||
var iconEl = header.findElement(Selector.outboxItemSyncIcon);
|
||||
var className = iconEl.getAttribute('className');
|
||||
if (/-syncing$/.test(className)) {
|
||||
return 'syncing';
|
||||
} else if (/-error$/.test(className)) {
|
||||
return 'error';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
switchAccount: function(number) {
|
||||
var accountSelector = '.fld-accountlist-container ' +
|
||||
'a:nth-child(' + number + ')';
|
||||
this.client.helper
|
||||
.waitForElement(accountSelector)
|
||||
.tap();
|
||||
this._waitForTransitionEnd('folder_picker');
|
||||
},
|
||||
|
||||
tapSettingsButton: function() {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.settingsButton)
|
||||
.tap();
|
||||
this._waitForTransitionEnd('settings_main');
|
||||
},
|
||||
|
||||
tapSettingsDoneButton: function() {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.settingsDoneButton)
|
||||
.tap();
|
||||
this._waitForTransitionEnd('folder_picker');
|
||||
},
|
||||
|
||||
tapSettingsAccountIndex: function(index) {
|
||||
var elements = this.client.findElements(Selector.settingsMainAccountItems);
|
||||
this.client.helper.waitForElement(elements[index]).tap();
|
||||
this._waitForTransitionEnd('settings_account');
|
||||
},
|
||||
|
||||
tapAddAccountButton: function() {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.addAccountButton)
|
||||
.tap();
|
||||
this._waitForTransitionEnd('setup_account_info');
|
||||
},
|
||||
|
||||
tapNotifyEmailCheckbox: function() {
|
||||
this._tapSelector(Selector.notifyEmailCheckbox);
|
||||
},
|
||||
|
||||
tapAccountSettingsBackButton: function() {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.accountSettingsBackButton)
|
||||
.tap();
|
||||
this._waitForTransitionEnd('settings_main');
|
||||
},
|
||||
|
||||
tapCompose: function() {
|
||||
this._tapSelector(Selector.composeButton);
|
||||
this.waitForCompose();
|
||||
},
|
||||
|
||||
typeTo: function(email) {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.composeEmailInput)
|
||||
.sendKeys(email);
|
||||
},
|
||||
|
||||
typeSubject: function(subject) {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.composeSubjectInput)
|
||||
.sendKeys(subject);
|
||||
},
|
||||
|
||||
typeBody: function(string) {
|
||||
var bodyInput = this.client.
|
||||
findElement(Selector.composeBodyInput);
|
||||
bodyInput.click();
|
||||
bodyInput.sendKeys(string);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the textual representation of the contenteditable that is our
|
||||
* compose area using the app logic that does this for when we save and
|
||||
* send the draft. Using innerHTML would not be realistic and textContent
|
||||
* over-simplifies our newline handling as well as risking oddities in the
|
||||
* future if we inline various affordances proposed by UX.
|
||||
*/
|
||||
getComposeBody: function() {
|
||||
return this.client.executeScript(function() {
|
||||
var cards = window.wrappedJSObject.require('cards'),
|
||||
card = cards._cardStack[cards.activeCardIndex];
|
||||
if (card.nodeName.toLowerCase() !== 'cards-compose') {
|
||||
throw new Error('active card should be compose!');
|
||||
}
|
||||
|
||||
return card.fromEditor();
|
||||
}, []);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enters edit mode.
|
||||
* Waits for an edit checkbox to appear.
|
||||
*/
|
||||
editMode: function() {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.editMode)
|
||||
.tap();
|
||||
|
||||
this.client.helper
|
||||
.waitForElement(Selector.editModeCheckBoxes);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the edit mode checkboxes.
|
||||
*/
|
||||
editModeCheckboxes: function() {
|
||||
var elements = this.client.findElements(Selector.editModeCheckBoxes);
|
||||
return elements;
|
||||
},
|
||||
|
||||
isElementDisabled: function(selector) {
|
||||
var client = this.client;
|
||||
|
||||
client.helper.waitForElement(selector);
|
||||
|
||||
client.waitFor(function() {
|
||||
return client.executeScript(function(selector) {
|
||||
var doc = window.wrappedJSObject.document,
|
||||
selectNode = doc.querySelector(selector);
|
||||
|
||||
return selectNode.disabled;
|
||||
}, [selector]);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Taps the trash button in edit mode.
|
||||
*/
|
||||
isEditModeTrashDisabled: function() {
|
||||
this.isElementDisabled(Selector.editModeTrash);
|
||||
},
|
||||
|
||||
abortCompose: function(cardId) {
|
||||
this._waitForElementNoTransition(Selector.composeBackButton).tap();
|
||||
this._waitForElementNoTransition(Selector.composeDraftDiscard).tap();
|
||||
this._waitForTransitionEnd(cardId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the visible input elements for the current card.
|
||||
*/
|
||||
getVisibleCardInputs: function() {
|
||||
var elements = this.client.findElements(Selector.currentCardInputs)
|
||||
.filter(function(element) {
|
||||
return element.displayed();
|
||||
});
|
||||
return elements;
|
||||
},
|
||||
|
||||
saveLocalDrafts: function() {
|
||||
this._waitForElementNoTransition(Selector.composeBackButton).tap();
|
||||
this._waitForElementNoTransition(Selector.composeDraftSave).tap();
|
||||
this._waitForTransitionEnd('message_list');
|
||||
},
|
||||
|
||||
tapSend: function() {
|
||||
var client = this.client;
|
||||
/*
|
||||
* We cannot tap the Selector.composeSendButton element with (0, 0) offset,
|
||||
* because the attachment button covers the left side of it a little bit.
|
||||
*
|
||||
* We could refer the css style in:
|
||||
* https://github.com/mozilla-b2g/gaia/blob/master/shared/style/headers.css#L117
|
||||
* And the patch in http://bugzil.la/907061 make us skip the issue luckily.
|
||||
*
|
||||
* We discuss and fix the issue in http://bugzil.la/907092
|
||||
*/
|
||||
client.helper
|
||||
.waitForElement(Selector.composeSendButton)
|
||||
.tap();
|
||||
// wait for being in the email list page
|
||||
this._waitForElementNoTransition(Selector.refreshButton);
|
||||
this.waitForMessageList();
|
||||
},
|
||||
|
||||
tapRefreshButton: function() {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.refreshButton)
|
||||
.tap();
|
||||
},
|
||||
|
||||
tapSearchArea: function() {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.searchTextTease)
|
||||
.sendKeys('a');
|
||||
|
||||
|
||||
var client = this.client;
|
||||
client.waitFor(function() {
|
||||
return client.executeScript(function(selector) {
|
||||
var doc = window.wrappedJSObject.document,
|
||||
selectNode = doc.querySelector(selector);
|
||||
|
||||
// Synthesize an event since focus does not work
|
||||
// through marionette API
|
||||
var event = document.createEvent('Event');
|
||||
event.initEvent('focus', true, true);
|
||||
selectNode.dispatchEvent(event);
|
||||
|
||||
return true;
|
||||
}, [Selector.searchTextTease]);
|
||||
});
|
||||
|
||||
this.client.helper
|
||||
.waitForElement(Selector.searchCard);
|
||||
this._waitForTransitionEnd('message_list_search');
|
||||
},
|
||||
|
||||
waitForMessageList: function() {
|
||||
this._waitForTransitionEnd('message_list');
|
||||
},
|
||||
|
||||
waitForMessageReader: function() {
|
||||
this._waitForTransitionEnd('message_reader');
|
||||
},
|
||||
|
||||
waitForCompose: function() {
|
||||
this._waitForTransitionEnd('compose');
|
||||
},
|
||||
|
||||
waitForSynchronized: function(index) {
|
||||
this.getHeaderAtIndex(index);
|
||||
},
|
||||
|
||||
launch: function() {
|
||||
var client = this.client;
|
||||
client.apps.launch(Email.EMAIL_ORIGIN);
|
||||
client.apps.switchToApp(Email.EMAIL_ORIGIN);
|
||||
// wait for the document body to know we're really launched
|
||||
client.helper.waitForElement('body');
|
||||
},
|
||||
|
||||
close: function() {
|
||||
var client = this.client;
|
||||
client.apps.close(Email.EMAIL_ORIGIN);
|
||||
},
|
||||
|
||||
getHeaderAtIndex: function(index) {
|
||||
var element = this.client.findElement(Selector.messageHeaderItem +
|
||||
'[data-index="' + index + '"]');
|
||||
return this.client.helper.waitForElement(element);
|
||||
},
|
||||
|
||||
tapEmailAtIndex: function(index) {
|
||||
var element = this.getHeaderAtIndex(index);
|
||||
element.tap();
|
||||
this.waitForMessageReader();
|
||||
},
|
||||
|
||||
tapEmailBySubject: function(subject, cardId) {
|
||||
// The emails may not be present in the list yet. So keep checking until
|
||||
// we see one. Then tap on it.
|
||||
this.client.waitFor(function() {
|
||||
var element = this.getEmailBySubject(subject);
|
||||
if (!element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
element.tap();
|
||||
this._waitForTransitionEnd(cardId);
|
||||
return true;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getMessageCount: function() {
|
||||
return this.client.findElements(Selector.messageHeaderItem).length;
|
||||
},
|
||||
|
||||
getEmailBySubject: function(subject) {
|
||||
var messageHeaders = this.client.findElements(Selector.messageHeaderItem),
|
||||
messageHeadersLength = messageHeaders.length,
|
||||
element;
|
||||
|
||||
for (var i = 0; i < messageHeadersLength; i++) {
|
||||
var header = this.client.helper
|
||||
.waitForChild(messageHeaders[i], '.msg-header-subject')
|
||||
.text();
|
||||
if (header === subject) {
|
||||
element = messageHeaders[i];
|
||||
}
|
||||
}
|
||||
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the reply menu and selects 'reply', 'all', or 'forward'.
|
||||
*/
|
||||
tapReply: function(mode) {
|
||||
var client = this.client;
|
||||
// open the reply menu
|
||||
client.helper.waitForElement(Selector.replyMenuButton).tap();
|
||||
client.helper.waitForElement(Selector.replyMenu);
|
||||
// select the appropriate option
|
||||
var whichButton;
|
||||
switch (mode) {
|
||||
case 'all':
|
||||
whichButton = Selector.replyMenuAll;
|
||||
break;
|
||||
case 'forward':
|
||||
whichButton = Selector.replyMenuForward;
|
||||
break;
|
||||
case 'reply':
|
||||
whichButton = Selector.replyMenuReply;
|
||||
break;
|
||||
default:
|
||||
whichButton = Selector.replyMenuReply;
|
||||
break;
|
||||
}
|
||||
client.helper.waitForElement(whichButton).tap();
|
||||
this._waitForTransitionEnd('compose');
|
||||
},
|
||||
|
||||
setSyncIntervalSelectValue: function(value) {
|
||||
return this._setSelectValue(Selector.syncIntervalSelect, value);
|
||||
},
|
||||
|
||||
// TODO: switch to https://github.com/mozilla-b2g/marionette-plugin-forms
|
||||
// once this bug is fixed:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=915324
|
||||
_setSelectValue: function(selector, value) {
|
||||
var client = this.client;
|
||||
client.waitFor(function() {
|
||||
return client.executeScript(function(selector, value) {
|
||||
var doc = window.wrappedJSObject.document,
|
||||
selectNode = doc.querySelector(selector);
|
||||
|
||||
selectNode.value = value;
|
||||
|
||||
// Synthesize an event since changing the value on its own does
|
||||
// not trigger change listeners.
|
||||
var event = document.createEvent('Event');
|
||||
event.initEvent('change', true, true);
|
||||
selectNode.dispatchEvent(event);
|
||||
|
||||
return true;
|
||||
}, [selector, value]);
|
||||
});
|
||||
},
|
||||
|
||||
_onTransitionEndScriptTimeout: function(cardId) {
|
||||
var result = this.client.executeScript(function(cardId) {
|
||||
var cards = window.wrappedJSObject.require('cards'),
|
||||
htmlCache = window.wrappedJSObject.require('html_cache'),
|
||||
cardNode = cards._cardStack[cards.activeCardIndex];
|
||||
|
||||
return {
|
||||
cardNode: !!cardNode,
|
||||
centered: cardNode && cardNode.classList.contains('center'),
|
||||
correctId: cardNode && htmlCache.nodeToKey(cardNode) === cardId,
|
||||
eventsClear: !cards._eatingEventsUntilNextCard
|
||||
};
|
||||
}, [cardId]);
|
||||
|
||||
console.log('TRANSITION END TIMEOUT:');
|
||||
console.log(JSON.stringify(result, null, ' '));
|
||||
},
|
||||
|
||||
_waitForTransitionEnd: function(cardId) {
|
||||
var client = this.client;
|
||||
|
||||
// To find out what is wrong with an intermittent failure in here,
|
||||
// log the script test criteria
|
||||
client.onScriptTimeout = this._onTransitionEndScriptTimeout
|
||||
.bind(this, cardId);
|
||||
|
||||
client.waitFor(function() {
|
||||
return client.executeScript(function(cardId) {
|
||||
var cards = window.wrappedJSObject.require('cards'),
|
||||
htmlCache = window.wrappedJSObject.require('html_cache'),
|
||||
cardNode = cards._cardStack[cards.activeCardIndex];
|
||||
|
||||
return !!cardNode && cardNode.classList.contains('center') &&
|
||||
htmlCache.nodeToKey(cardNode) === cardId &&
|
||||
!cards._eatingEventsUntilNextCard;
|
||||
}, [cardId]);
|
||||
});
|
||||
|
||||
client.onScriptTimeout = null;
|
||||
},
|
||||
|
||||
_onNoTransitionScriptTimeout: function() {
|
||||
var result = this.client.executeScript(function() {
|
||||
var cards = window.wrappedJSObject.require('cards');
|
||||
|
||||
return {
|
||||
cards: !!cards,
|
||||
eventsClear: !!cards && !cards._eatingEventsUntilNextCard
|
||||
};
|
||||
});
|
||||
|
||||
console.log('NO TRANSITION TIMEOUT:');
|
||||
console.log(JSON.stringify(result, null, ' '));
|
||||
},
|
||||
|
||||
_waitForNoTransition: function() {
|
||||
var client = this.client;
|
||||
|
||||
// To find out what is wrong with an intermittent failure in here,
|
||||
// log the script test criteria
|
||||
client.onScriptTimeout = this._onNoTransitionScriptTimeout
|
||||
.bind(this);
|
||||
|
||||
client.waitFor(function() {
|
||||
return client.executeScript(function() {
|
||||
var cards = window.wrappedJSObject.require('cards');
|
||||
return !cards._eatingEventsUntilNextCard;
|
||||
});
|
||||
});
|
||||
|
||||
client.onScriptTimeout = null;
|
||||
},
|
||||
|
||||
_setupTypeName: function(name) {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.setupNameInput)
|
||||
.sendKeys(name);
|
||||
},
|
||||
|
||||
_setupTypeEmail: function(email) {
|
||||
this.client.helper
|
||||
.waitForElement(Selector.setupEmailInput)
|
||||
.sendKeys(email);
|
||||
},
|
||||
|
||||
_waitForElementNoTransition: function(selector) {
|
||||
this._waitForNoTransition();
|
||||
return this.client.helper.waitForElement(selector);
|
||||
},
|
||||
|
||||
_tapSelector: function(selector) {
|
||||
this.client.helper.waitForElement(selector).tap();
|
||||
},
|
||||
|
||||
_tapNext: function(selector, cardId) {
|
||||
this._tapSelector(selector);
|
||||
if (cardId) {
|
||||
this._waitForTransitionEnd(cardId);
|
||||
}
|
||||
},
|
||||
|
||||
_clearAndSendKeys: function(selector, value) {
|
||||
var el = this.client.helper.waitForElement(selector);
|
||||
el.clear();
|
||||
el.sendKeys(value);
|
||||
},
|
||||
|
||||
_manualSetupTypeName: function(name) {
|
||||
this._clearAndSendKeys(Selector.manualSetupNameInput, name);
|
||||
},
|
||||
|
||||
_manualSetupTypeEmail: function(email) {
|
||||
this._clearAndSendKeys(Selector.manualSetupEmailInput, email);
|
||||
},
|
||||
|
||||
_manualSetupTypeImapPassword: function(password) {
|
||||
this._clearAndSendKeys(Selector.manualSetupImapPasswordInput, password);
|
||||
},
|
||||
|
||||
_manualSetupTypeSmtpPassword: function(password) {
|
||||
this._clearAndSendKeys(Selector.manualSetupSmtpPasswordInput, password);
|
||||
},
|
||||
|
||||
_manualSetupTypeImapUsername: function(name) {
|
||||
this._clearAndSendKeys(Selector.manualSetupImapUsernameInput, name);
|
||||
},
|
||||
|
||||
_manualSetupTypeImapHostname: function(hostname) {
|
||||
this._clearAndSendKeys(Selector.manualSetupImapHostnameInput, hostname);
|
||||
},
|
||||
|
||||
_manualSetupTypeImapPort: function(port) {
|
||||
this._clearAndSendKeys(Selector.manualSetupImapPortInput, port);
|
||||
},
|
||||
|
||||
_manualSetupTypeSmtpUsername: function(name) {
|
||||
this._clearAndSendKeys(Selector.manualSetupSmtpUsernameInput, name);
|
||||
},
|
||||
|
||||
_manualSetupTypeSmtpHostname: function(hostname) {
|
||||
this._clearAndSendKeys(Selector.manualSetupSmtpHostnameInput, hostname);
|
||||
},
|
||||
|
||||
_manualSetupTypeSmtpPort: function(port) {
|
||||
this._clearAndSendKeys(Selector.manualSetupSmtpPortInput, port);
|
||||
},
|
||||
|
||||
/**
|
||||
* Because we never expose "plain" (zero security for users) as an option we
|
||||
* need to hack the html to expose it (the backend will know about this).
|
||||
*/
|
||||
_manualSetupUpdateSocket: function(type) {
|
||||
var element = this.client.helper.waitForElement(Selector[type]);
|
||||
|
||||
// select is a real dom select element
|
||||
element.scriptWith(function(select) {
|
||||
// create the option
|
||||
var option = document.createElement('option');
|
||||
option.value = 'plain';
|
||||
select.add(option, select.options[select.options.length - 1]);
|
||||
|
||||
// update the form to plain so we can use insecure sockets for the
|
||||
// fakeserver.
|
||||
select.value = 'plain';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
require('./debug')('email', Email.prototype);
|
||||
|
||||
68
gaia/apps/email/test/marionette/lib/email_data.js
Executable file
68
gaia/apps/email/test/marionette/lib/email_data.js
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
/*jshint node: true, browser: true */
|
||||
/*global marionetteScriptFinished */
|
||||
|
||||
/**
|
||||
* Manipulates email's data APIs in the app.
|
||||
*/
|
||||
function EmailData(client) {
|
||||
this.client = client;
|
||||
}
|
||||
module.exports = EmailData;
|
||||
|
||||
EmailData.prototype = {
|
||||
/**
|
||||
* Gets the current account for the given account ID. Just returns the
|
||||
* _wireRep as that will survive the serialization hops.
|
||||
* @param {String} accountId
|
||||
* @return {Object} the account._wireRep object, or undefined if not found.
|
||||
*/
|
||||
getCurrentAccount: function() {
|
||||
var client = this.client;
|
||||
return client.executeAsyncScript(function() {
|
||||
var model = window.wrappedJSObject.require('model_create').defaultModel;
|
||||
model.latest('account', function(account) {
|
||||
marionetteScriptFinished(account._wireRep);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current account for the given account ID. Just returns the
|
||||
* _wireRep as that will survive the serialization hops.
|
||||
* @param {String} accountId
|
||||
* @return {Object} the account._wireRep object, or undefined if not found.
|
||||
*/
|
||||
waitForCurrentAccountUpdate: function(key, value) {
|
||||
var client = this.client;
|
||||
return client.executeAsyncScript(function(key, value) {
|
||||
|
||||
var account,
|
||||
model = window.wrappedJSObject.require('model_create').defaultModel,
|
||||
acctsSlice = model.api.viewAccounts(false);
|
||||
|
||||
function checkDone() {
|
||||
if (account[key] === value) {
|
||||
acctsSlice.die();
|
||||
marionetteScriptFinished(true);
|
||||
}
|
||||
}
|
||||
|
||||
acctsSlice.oncomplete = function() {
|
||||
account = acctsSlice.defaultAccount;
|
||||
checkDone();
|
||||
};
|
||||
|
||||
acctsSlice.onchange = function(item) {
|
||||
if (account) {
|
||||
if (account.id === item.id) {
|
||||
checkDone();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}, [key, value]);
|
||||
}
|
||||
};
|
||||
|
||||
require('./debug')('email_data', EmailData.prototype);
|
||||
44
gaia/apps/email/test/marionette/lib/email_sync.js
Executable file
44
gaia/apps/email/test/marionette/lib/email_sync.js
Executable file
|
|
@ -0,0 +1,44 @@
|
|||
'use strict';
|
||||
/*jshint node: true, browser: true */
|
||||
var SHARED_PATH = __dirname + '/../../../../../shared/test/integration';
|
||||
|
||||
function EmailSync(client) {
|
||||
this.client = client;
|
||||
}
|
||||
module.exports = EmailSync;
|
||||
|
||||
EmailSync.prototype = {
|
||||
/**
|
||||
* Call this in the setup() for the test.
|
||||
*/
|
||||
|
||||
setup: function() {
|
||||
this.client.contentScript.inject(
|
||||
SHARED_PATH + '/mock_navigator_moz_set_message_handler.js'
|
||||
);
|
||||
this.client.contentScript.inject(
|
||||
SHARED_PATH + '/mock_navigator_mozalarms.js'
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Does the work to trigger a sync using helpers in
|
||||
* mock_navigator_moz_set_message_handler.js.
|
||||
*/
|
||||
triggerSync: function() {
|
||||
// trigger sync in Email App
|
||||
this.client.executeScript(function() {
|
||||
var interval = 1000;
|
||||
var date = new Date(Date.now() + interval).getTime();
|
||||
var alarm = {
|
||||
data: {
|
||||
type: 'sync',
|
||||
accountIds: ['0', '1'],
|
||||
interval: interval,
|
||||
timestamp: date
|
||||
}
|
||||
};
|
||||
return window.wrappedJSObject.fireMessageHandler(alarm, 'alarm');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*global Components, Services */
|
||||
'use strict';
|
||||
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
Services.obs.addObserver(function(document) {
|
||||
if (!document || !document.location) {
|
||||
return;
|
||||
}
|
||||
|
||||
var window = document.defaultView.wrappedJSObject;
|
||||
var messageHandler;
|
||||
|
||||
window.navigator.__defineGetter__('mozHasPendingMessage', function() {
|
||||
return function(type) {
|
||||
return type === 'activity';
|
||||
};
|
||||
});
|
||||
|
||||
window.navigator.__defineGetter__('mozSetMessageHandler', function() {
|
||||
return function(type, callback) {
|
||||
if (type === 'activity') {
|
||||
messageHandler = callback;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
window.__defineGetter__('fireMessageHandler', function() {
|
||||
return function(data) {
|
||||
if (messageHandler && typeof(messageHandler) === 'function') {
|
||||
messageHandler(data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
});
|
||||
}, 'document-element-inserted', false);
|
||||
98
gaia/apps/email/test/marionette/lib/notification.js
Executable file
98
gaia/apps/email/test/marionette/lib/notification.js
Executable file
|
|
@ -0,0 +1,98 @@
|
|||
/*jshint node: true, browser: true */
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert'),
|
||||
containerSelector = '#desktop-notifications-container .other-notifications';
|
||||
|
||||
function Notification(client) {
|
||||
this.client = client;
|
||||
}
|
||||
module.exports = Notification;
|
||||
|
||||
Notification.prototype = {
|
||||
getContainer: function() {
|
||||
return this.client.findElement(containerSelector);
|
||||
},
|
||||
|
||||
assertNoNotification: function() {
|
||||
this.getContainer().findElement('div', function(error) {
|
||||
assert.ok(!!error);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Uses Notification.get to get the first notification to show up. This
|
||||
* approach is used instead of something like tapping on the notification,
|
||||
* since the notification UI is normally hidden and it is non-trivial to open,
|
||||
* and the data API really reflects the result of the notification work done
|
||||
* by the email app.
|
||||
*/
|
||||
triggerFirstNotification: function() {
|
||||
var client = this.client;
|
||||
return client.executeAsyncScript(function() {
|
||||
function getFirstNotification() {
|
||||
window.Notification.get().then(function(notifications) {
|
||||
if (!notifications) {
|
||||
marionetteScriptFinished(false);
|
||||
} else if (!notifications.length) {
|
||||
// The check was done too fast. Wait for some time to pass
|
||||
// and check again.
|
||||
window.setTimeout(getFirstNotification, 300);
|
||||
} else {
|
||||
var notification = notifications[0],
|
||||
evt = window.wrappedJSObject.require('evt');
|
||||
|
||||
evt.emit('notification', {
|
||||
clicked: true,
|
||||
imageURL: notification.imageURL,
|
||||
data: notification.data,
|
||||
tag: notification.tag
|
||||
});
|
||||
|
||||
marionetteScriptFinished(true);
|
||||
}
|
||||
}).catch(function(error) {
|
||||
console.error('Notification.get failed with: ' + error);
|
||||
marionetteScriptFinished(false);
|
||||
});
|
||||
}
|
||||
|
||||
getFirstNotification();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Uses Notification.get to get the first notification's data.
|
||||
*/
|
||||
getFirstNotificationData: function() {
|
||||
var client = this.client;
|
||||
return client.executeAsyncScript(function() {
|
||||
function getFirstNotification() {
|
||||
window.Notification.get().then(function(notifications) {
|
||||
if (!notifications) {
|
||||
marionetteScriptFinished(false);
|
||||
} else if (!notifications.length) {
|
||||
// The check was done too fast. Wait for some time to pass
|
||||
// and check again.
|
||||
window.setTimeout(getFirstNotification, 300);
|
||||
} else {
|
||||
var notification = notifications[0];
|
||||
|
||||
marionetteScriptFinished({
|
||||
imageURL: notification.imageURL,
|
||||
data: notification.data,
|
||||
tag: notification.tag
|
||||
});
|
||||
}
|
||||
}).catch(function(error) {
|
||||
console.error('Notification.get failed with: ' + error);
|
||||
marionetteScriptFinished(false);
|
||||
});
|
||||
}
|
||||
|
||||
getFirstNotification();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
require('./debug')('notification', Notification.prototype);
|
||||
52
gaia/apps/email/test/marionette/lib/server_helper.js
Executable file
52
gaia/apps/email/test/marionette/lib/server_helper.js
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Primary interface to expose server details to tests. Internally abstracts the
|
||||
* details of what kind of server (aside from general protocol) so we can also
|
||||
* use fake servers.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Example interface for server setup.
|
||||
*
|
||||
* var object = require('exported_module').use(options, this);
|
||||
*
|
||||
* // object is expected to have one or more top level server types exposed.
|
||||
*
|
||||
* // example imap interface
|
||||
* object.imap.port
|
||||
* object.imap.username
|
||||
* object.imap.hostname
|
||||
* object.imap.password
|
||||
*
|
||||
* // example imap interface
|
||||
* object.smtp.port
|
||||
* object.smtp.username
|
||||
* object.smtp.hostname
|
||||
* object.smtp.password
|
||||
*
|
||||
* While the "port" may be optional (you might need a url instead)
|
||||
* username/password should always be included.
|
||||
*
|
||||
*/
|
||||
//function serverSetup(options) {};
|
||||
|
||||
'use strict';
|
||||
/* jshint node: true */
|
||||
|
||||
/**
|
||||
* Expose a server lifecycle to a given test.
|
||||
*
|
||||
* marionette('xfoo', function() {
|
||||
* require('./lib/server_helper').use({}, this);
|
||||
* });
|
||||
*
|
||||
* @param {Object} options for server.
|
||||
* @param {Object} mochaContext
|
||||
* (usually the |this| of a marionette/suite block).
|
||||
*/
|
||||
function determineServer(options, mochaContext) {
|
||||
// XXX: right now this is hardcoded to the fake imap server logic.
|
||||
return require('./servers/fakeimap').use(options, mochaContext);
|
||||
}
|
||||
|
||||
module.exports.use = determineServer;
|
||||
99
gaia/apps/email/test/marionette/lib/servers/fakeimap.js
Executable file
99
gaia/apps/email/test/marionette/lib/servers/fakeimap.js
Executable file
|
|
@ -0,0 +1,99 @@
|
|||
'use strict';
|
||||
/* jshint node: true */
|
||||
|
||||
var server = require('mail-fakeservers');
|
||||
|
||||
var DEFAULT_OPTIONS = Object.freeze({
|
||||
// convention from GELAM
|
||||
credentials: {
|
||||
username: 'testy',
|
||||
password: 'testy'
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Updates given object with the state from an imapStack.
|
||||
*
|
||||
* @param {Object} state target.
|
||||
* @param {Object} stack to pull updates from.
|
||||
* @param {Object} options for setup.
|
||||
*/
|
||||
function updateState(state, stack, options) {
|
||||
if (!options) {
|
||||
options = DEFAULT_OPTIONS;
|
||||
}
|
||||
|
||||
state.imap = {
|
||||
port: stack.imapPort,
|
||||
username: options.credentials.username,
|
||||
password: options.credentials.password,
|
||||
hostname: 'localhost'
|
||||
};
|
||||
state.smtp = {
|
||||
port: stack.smtpPort,
|
||||
username: 'outgoingUsername' in options.credentials ?
|
||||
options.credentials.outgoingUsername : options.credentials.username,
|
||||
password: 'outgoingPassword' in options.credentials ?
|
||||
options.credentials.outgoingPassword : options.credentials.password,
|
||||
hostname: 'localhost'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} options for setup.
|
||||
* @param {Object} mochaContext from callee.
|
||||
*/
|
||||
function use(options, mochaContext) {
|
||||
/**
|
||||
* Reused for every test/setup/teardown contains internal state so tests can
|
||||
* just directly reference the same object instead of manging state
|
||||
* themselves.
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
var state = {};
|
||||
|
||||
// spawns servers
|
||||
var controlServer;
|
||||
|
||||
// current imap/smtp servers
|
||||
var imapStack;
|
||||
|
||||
if (options === null) {
|
||||
options = DEFAULT_OPTIONS;
|
||||
}
|
||||
|
||||
suiteSetup(function(done) {
|
||||
this.timeout('20s');
|
||||
server.create(function(err, control) {
|
||||
controlServer = control;
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
// we need a new stack each test
|
||||
setup(function(done) {
|
||||
controlServer.createImapStack(options, function(err, imap) {
|
||||
// update the state information
|
||||
updateState(state, imap, options);
|
||||
|
||||
imapStack = imap;
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
// clear away old servers
|
||||
teardown(function(done) {
|
||||
controlServer.cleanupStacks(done);
|
||||
});
|
||||
|
||||
// so node can exit close the server processes.
|
||||
suiteTeardown(function() {
|
||||
controlServer.kill();
|
||||
});
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
module.exports.use = use;
|
||||
81
gaia/apps/email/test/marionette/local_drafts_test.js
Executable file
81
gaia/apps/email/test/marionette/local_drafts_test.js
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
'use strict';
|
||||
|
||||
var Email = require('./lib/email'),
|
||||
assert = require('assert'),
|
||||
serverHelper = require('./lib/server_helper');
|
||||
|
||||
var EMAIL_ADDRESS = 'firefox-os-drafts@example.com',
|
||||
EMAIL_SUBJECT = 'I still have a dream';
|
||||
|
||||
marionette('local draft', function() {
|
||||
var app;
|
||||
var client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
var server = serverHelper.use(null, this);
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
app.launch();
|
||||
app.manualSetupImapEmail(server);
|
||||
|
||||
// Navigate to local drafts.
|
||||
app.tapFolderListButton();
|
||||
app.tapLocalDraftsItem();
|
||||
|
||||
// Save a local draft.
|
||||
app.tapCompose();
|
||||
app.typeTo(EMAIL_ADDRESS);
|
||||
app.typeSubject(EMAIL_SUBJECT);
|
||||
app.saveLocalDrafts();
|
||||
});
|
||||
|
||||
test.skip('should show the correct email address', function() {
|
||||
// Navigate to local drafts.
|
||||
app.tapFolderListButton();
|
||||
app.tapLocalDraftsItem();
|
||||
|
||||
var message = app.getEmailBySubject(EMAIL_SUBJECT);
|
||||
if (!message) {
|
||||
throw new Error('Could not find local draft!');
|
||||
}
|
||||
|
||||
// TODO(gaye): Abstract this waitForChild into lib/email.
|
||||
var author = client.helper
|
||||
.waitForChild(message, '.msg-header-author')
|
||||
.text();
|
||||
assert.equal(author, EMAIL_ADDRESS);
|
||||
});
|
||||
|
||||
test('should maintain formatting', function() {
|
||||
var NAME = 'FireFox OS';
|
||||
var EMAIL_ADDRESS = 'firefox-os-drafts@example.com';
|
||||
var MAILBOX = NAME + ' <' + EMAIL_ADDRESS + '>';
|
||||
var EMAIL_SUBJECT = 'I still have a linebreak';
|
||||
var SPACE = ' ';
|
||||
var BODY = ' many spaces: newline:\nmoar newlines:\n\n\nyeah!\n' +
|
||||
'and unicodes: Sssś Lałalalala\n' +
|
||||
'say no to <b><i>HTML</b>';
|
||||
|
||||
// go to the Local Drafts page
|
||||
app.tapFolderListButton();
|
||||
app.tapLocalDraftsItem();
|
||||
|
||||
// save a local draft
|
||||
app.tapCompose();
|
||||
app.typeTo(MAILBOX);
|
||||
// type a space to create the bubble
|
||||
app.typeTo(SPACE);
|
||||
app.typeSubject(EMAIL_SUBJECT);
|
||||
app.typeBody(BODY);
|
||||
|
||||
// verify that what we typed is what we get back
|
||||
assert.equal(app.getComposeBody(), BODY);
|
||||
|
||||
app.saveLocalDrafts();
|
||||
|
||||
// make sure the subject and body came back unchanged
|
||||
app.tapEmailBySubject(EMAIL_SUBJECT, 'compose');
|
||||
assert.equal(app.getComposeBody(), BODY);
|
||||
});
|
||||
});
|
||||
28
gaia/apps/email/test/marionette/manual_setup_outgoing_credentials_test.js
Executable file
28
gaia/apps/email/test/marionette/manual_setup_outgoing_credentials_test.js
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
var Email = require('./lib/email');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
marionette('Use a different outgoing password', function() {
|
||||
var app,
|
||||
client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
app.launch();
|
||||
});
|
||||
|
||||
var server = serverHelper.use({
|
||||
credentials: {
|
||||
username: 'testy',
|
||||
password: 'testy',
|
||||
outgoingPassword: 'outgoingtesty'
|
||||
}
|
||||
}, this);
|
||||
|
||||
test('should be able to manually set up email', function() {
|
||||
app.manualSetupImapEmail(server);
|
||||
});
|
||||
});
|
||||
|
||||
31
gaia/apps/email/test/marionette/message_list_test.js
Executable file
31
gaia/apps/email/test/marionette/message_list_test.js
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
'use strict';
|
||||
|
||||
var Email = require('./lib/email');
|
||||
var assert = require('assert');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
marionette('email message list edit mode', function() {
|
||||
var app;
|
||||
|
||||
var client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
|
||||
var server = serverHelper.use(null, this);
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
app.launch();
|
||||
app.manualSetupImapEmail(server);
|
||||
app.sendAndReceiveMessages([
|
||||
{ to: 'testy@localhost', subject: 'Only One Lonely', body: 'Fish' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('trash is disabled when no selected messages', function() {
|
||||
assert.ok(true);
|
||||
app.editMode();
|
||||
app.isEditModeTrashDisabled();
|
||||
});
|
||||
|
||||
});
|
||||
172
gaia/apps/email/test/marionette/next_previous_test.js
Executable file
172
gaia/apps/email/test/marionette/next_previous_test.js
Executable file
|
|
@ -0,0 +1,172 @@
|
|||
'use strict';
|
||||
var Email = require('./lib/email');
|
||||
var assert = require('assert');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether or not this has a <span class="... icon-disabled ...">
|
||||
* in its DOM subtree.
|
||||
*
|
||||
* @param {Marionette.Element} button one of msgDownBtn or msgUpButton.
|
||||
* @return {boolean} whether or not the param is enabled.
|
||||
*/
|
||||
function isHeaderButtonEnabled(button) {
|
||||
var span = button.findElement('span');
|
||||
var className = span.getAttribute('className');
|
||||
return className.indexOf('icon-disabled') === -1;
|
||||
}
|
||||
|
||||
marionette('email next previous', function() {
|
||||
var app;
|
||||
var client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
var server = serverHelper.use(null, this);
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
app.launch();
|
||||
app.manualSetupImapEmail(server);
|
||||
app.sendAndReceiveMessages([
|
||||
{ to: 'testy@localhost', subject: 'One', body: 'Fish' },
|
||||
{ to: 'testy@localhost', subject: 'Two', body: 'Fish' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('should grey out up when no message above', function() {
|
||||
app.tapEmailAtIndex(0);
|
||||
var el = app.msgUpBtn;
|
||||
assert.ok(!isHeaderButtonEnabled(el));
|
||||
});
|
||||
|
||||
test('should grey out down when no message below', function() {
|
||||
app.tapEmailAtIndex(1);
|
||||
var el = app.msgDownBtn;
|
||||
assert.ok(!isHeaderButtonEnabled(el));
|
||||
});
|
||||
|
||||
test('should not grey out up when message above', function() {
|
||||
app.tapEmailAtIndex(1);
|
||||
var el = app.msgUpBtn;
|
||||
assert.ok(isHeaderButtonEnabled(el));
|
||||
});
|
||||
|
||||
test('should not grey out down when message below', function() {
|
||||
app.tapEmailAtIndex(0);
|
||||
var el = app.msgDownBtn;
|
||||
assert.ok(isHeaderButtonEnabled(el));
|
||||
});
|
||||
|
||||
test('should move up when up tapped', function() {
|
||||
app.tapEmailAtIndex(1);
|
||||
app.advanceMessageReader(true);
|
||||
var subject = app.getMessageReaderSubject();
|
||||
assert.strictEqual(subject, 'Two');
|
||||
});
|
||||
|
||||
test('should move down when down tapped', function() {
|
||||
app.tapEmailAtIndex(0);
|
||||
app.advanceMessageReader(false);
|
||||
var subject = app.getMessageReaderSubject();
|
||||
assert.strictEqual(subject, 'One');
|
||||
});
|
||||
|
||||
test('should not move up when up tapped if greyed out', function() {
|
||||
app.tapEmailAtIndex(0);
|
||||
try {
|
||||
app.advanceMessageReader(true);
|
||||
} catch (err) {
|
||||
if (err.type !== 'InvalidElementState') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
var subject = app.getMessageReaderSubject();
|
||||
assert.strictEqual(subject, 'Two');
|
||||
});
|
||||
|
||||
test('should not move down when down tapped if greyed out', function() {
|
||||
app.tapEmailAtIndex(1);
|
||||
try {
|
||||
app.advanceMessageReader(false);
|
||||
} catch (err) {
|
||||
if (err.type !== 'InvalidElementState') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
var subject = app.getMessageReaderSubject();
|
||||
assert.strictEqual(subject, 'One');
|
||||
});
|
||||
|
||||
suite('scroll', function() {
|
||||
|
||||
setup(function() {
|
||||
// We need more messages to exercise scrolling.
|
||||
app.sendAndReceiveMessages([
|
||||
{ to: 'testy@localhost', subject: 'Red', body: 'Fish' },
|
||||
{ to: 'testy@localhost', subject: 'Blue', body: 'Fish' },
|
||||
{ to: 'testy@localhost', subject: 'Three', body: 'Fish' },
|
||||
{ to: 'testy@localhost', subject: 'Four', body: 'Fish' },
|
||||
{ to: 'testy@localhost', subject: 'Even', body: 'Fish' },
|
||||
{ to: 'test@localhost', subject: 'More', body: 'Fish' }
|
||||
// Pass 7 because the outer setup step already added two messages.
|
||||
], 7);
|
||||
});
|
||||
|
||||
test('should scroll up when up tapped', function() {
|
||||
// Start by scrolling to the bottom of the scroll container.
|
||||
var header = app.getHeaderAtIndex(7);
|
||||
var offsetTop = header.getAttribute('offsetTop');
|
||||
client.executeScript(function(scrollTo) {
|
||||
var scrollContainer =
|
||||
document.getElementsByClassName('msg-list-scrollouter')[0];
|
||||
scrollContainer.scrollTop = scrollTo;
|
||||
}, [offsetTop]);
|
||||
|
||||
var scrollContainer = app.msgListScrollOuter;
|
||||
var initial = parseInt(scrollContainer.getAttribute('scrollTop'), 10);
|
||||
|
||||
// The toaster could steal our tap, since our desired target is at the
|
||||
// bottom of the scroll list. So wait for it to disappear first.
|
||||
app.waitForToaster();
|
||||
|
||||
app.tapEmailAtIndex(7);
|
||||
|
||||
// Advance up to the first message.
|
||||
while (true) {
|
||||
var button = app.msgUpBtn;
|
||||
if (!isHeaderButtonEnabled(button)) {
|
||||
break;
|
||||
}
|
||||
|
||||
app.advanceMessageReader(true);
|
||||
}
|
||||
|
||||
scrollContainer = app.msgListScrollOuter;
|
||||
var updated = parseInt(scrollContainer.getAttribute('scrollTop'), 10);
|
||||
assert.ok(updated < initial);
|
||||
});
|
||||
|
||||
test('should scroll down when down tapped', function() {
|
||||
var scrollContainer = app.msgListScrollOuter;
|
||||
var initial = parseInt(scrollContainer.getAttribute('scrollTop'), 10);
|
||||
app.tapEmailAtIndex(0);
|
||||
|
||||
// Advance down to the last message.
|
||||
while (true) {
|
||||
var button = app.msgDownBtn;
|
||||
if (!isHeaderButtonEnabled(button)) {
|
||||
break;
|
||||
}
|
||||
|
||||
app.advanceMessageReader(false);
|
||||
}
|
||||
|
||||
scrollContainer = app.msgListScrollOuter;
|
||||
var updated = parseInt(scrollContainer.getAttribute('scrollTop'), 10);
|
||||
assert.ok(updated > initial);
|
||||
});
|
||||
});
|
||||
});
|
||||
87
gaia/apps/email/test/marionette/notification_click_test.js
Executable file
87
gaia/apps/email/test/marionette/notification_click_test.js
Executable file
|
|
@ -0,0 +1,87 @@
|
|||
/*jshint node: true */
|
||||
/*global marionette, setup, test */
|
||||
'use strict';
|
||||
|
||||
var Email = require('./lib/email');
|
||||
var EmailSync = require('./lib/email_sync');
|
||||
var NotificationLib = require('./lib/notification');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
marionette('email notifications, click', function() {
|
||||
var app, sync, notification,
|
||||
client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
}),
|
||||
server1 = serverHelper.use({
|
||||
credentials: {
|
||||
username: 'testy1',
|
||||
password: 'testy1'
|
||||
}
|
||||
}, this),
|
||||
server2 = serverHelper.use({
|
||||
credentials: {
|
||||
username: 'testy2',
|
||||
password: 'testy2'
|
||||
}
|
||||
}, this);
|
||||
|
||||
function sendEmail(server) {
|
||||
var email = server.imap.username + '@' + server.imap.hostname;
|
||||
app.tapCompose();
|
||||
app.typeTo(email);
|
||||
app.typeSubject('test email');
|
||||
app.typeBody('I still have a dream.');
|
||||
app.tapSend();
|
||||
}
|
||||
|
||||
function configureAndSend(messageCount) {
|
||||
|
||||
// Set up testy1, send email to testy1, since smtp fakserver
|
||||
// is paired with the imap fakserver for that account. So,
|
||||
// no cross sending of email across fakeserver instances.
|
||||
app.manualSetupImapEmail(server1);
|
||||
|
||||
for (var i = 0; i < messageCount; i++) {
|
||||
sendEmail(server1);
|
||||
}
|
||||
|
||||
// Now set up second account, to confirm system notifications
|
||||
// are only triggered in certain situations.
|
||||
app.tapFolderListButton();
|
||||
app.tapSettingsButton();
|
||||
app.tapAddAccountButton();
|
||||
app.manualSetupImapEmail(server2);
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
notification = new NotificationLib(client);
|
||||
sync = new EmailSync(client);
|
||||
sync.setup();
|
||||
|
||||
app.launch();
|
||||
});
|
||||
|
||||
test('show message_reader for 1 message notification', function() {
|
||||
configureAndSend(1);
|
||||
|
||||
sync.triggerSync();
|
||||
|
||||
notification.triggerFirstNotification();
|
||||
|
||||
// Since a single message notification, should go to message_reader.
|
||||
app.waitForMessageReader();
|
||||
});
|
||||
|
||||
test('show message_list for multiple message notification',
|
||||
function() {
|
||||
configureAndSend(2);
|
||||
|
||||
sync.triggerSync();
|
||||
|
||||
notification.triggerFirstNotification();
|
||||
|
||||
// Since a single message notification, should go to message_reader.
|
||||
app.waitForMessageList();
|
||||
});
|
||||
});
|
||||
88
gaia/apps/email/test/marionette/notification_disable_test.js
Executable file
88
gaia/apps/email/test/marionette/notification_disable_test.js
Executable file
|
|
@ -0,0 +1,88 @@
|
|||
'use strict';
|
||||
/*jshint node: true */
|
||||
/*global marionette, setup, test, Notification:true */
|
||||
|
||||
var Email = require('./lib/email');
|
||||
var EmailData = require('./lib/email_data');
|
||||
var EmailSync = require('./lib/email_sync');
|
||||
var Notification = require('./lib/notification');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
marionette('email notifications, disable', function() {
|
||||
var app, sync,
|
||||
client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
}),
|
||||
server1 = serverHelper.use({
|
||||
credentials: {
|
||||
username: 'testy1',
|
||||
password: 'testy1'
|
||||
}
|
||||
}, this),
|
||||
server2 = serverHelper.use({
|
||||
credentials: {
|
||||
username: 'testy2',
|
||||
password: 'testy2'
|
||||
}
|
||||
}, this);
|
||||
|
||||
function sendEmail(server) {
|
||||
var email = server.imap.username + '@' + server.imap.hostname;
|
||||
app.tapCompose();
|
||||
app.typeTo(email);
|
||||
app.typeSubject('test email');
|
||||
app.typeBody('I still have a dream.');
|
||||
app.tapSend();
|
||||
}
|
||||
|
||||
function configureAndSend(messageCount) {
|
||||
// Set up testy1, send email to testy1, since smtp fakserver
|
||||
// is paired with the imap fakserver for that account. So,
|
||||
// no cross sending of email across fakeserver instances.
|
||||
app.manualSetupImapEmail(server1);
|
||||
|
||||
for (var i = 0; i < messageCount; i++) {
|
||||
sendEmail(server1);
|
||||
}
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
sync = new EmailSync(client);
|
||||
sync.setup();
|
||||
|
||||
app.launch();
|
||||
});
|
||||
|
||||
test('disable notification, but still sync', function() {
|
||||
configureAndSend(1);
|
||||
|
||||
// Open the prefs and turn off getting notifications.
|
||||
app.tapFolderListButton();
|
||||
app.tapSettingsButton();
|
||||
app.tapSettingsAccountIndex(0);
|
||||
app.tapNotifyEmailCheckbox();
|
||||
app.tapAccountSettingsBackButton();
|
||||
app.tapSettingsDoneButton();
|
||||
app.tapFolderListCloseButton();
|
||||
|
||||
// Wait for the account to report the pref change.
|
||||
var emailData = new EmailData(client);
|
||||
emailData.waitForCurrentAccountUpdate('notifyOnNew', false);
|
||||
|
||||
// Now set up second account, to confirm system notifications
|
||||
// are only triggered in certain situations.
|
||||
app.tapFolderListButton();
|
||||
app.tapSettingsButton();
|
||||
app.tapAddAccountButton();
|
||||
app.manualSetupImapEmail(server2);
|
||||
|
||||
sync.triggerSync();
|
||||
|
||||
// Go back to system app, make sure no notification shows up
|
||||
client.switchToFrame();
|
||||
|
||||
var notification = new Notification(client);
|
||||
notification.assertNoNotification();
|
||||
});
|
||||
});
|
||||
105
gaia/apps/email/test/marionette/notification_foreground_test.js
Executable file
105
gaia/apps/email/test/marionette/notification_foreground_test.js
Executable file
|
|
@ -0,0 +1,105 @@
|
|||
/*jshint node: true */
|
||||
/*global marionette, setup, test */
|
||||
'use strict';
|
||||
|
||||
var Email = require('./lib/email');
|
||||
var EmailSync = require('./lib/email_sync');
|
||||
var assert = require('assert');
|
||||
var NotificationLib = require('./lib/notification');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
marionette('email notifications, foreground', function() {
|
||||
var app, sync, notification,
|
||||
client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
}),
|
||||
server1 = serverHelper.use({
|
||||
credentials: {
|
||||
username: 'testy1',
|
||||
password: 'testy1'
|
||||
}
|
||||
}, this),
|
||||
server2 = serverHelper.use({
|
||||
credentials: {
|
||||
username: 'testy2',
|
||||
password: 'testy2'
|
||||
}
|
||||
}, this);
|
||||
|
||||
function sendEmail(server) {
|
||||
var email = server.imap.username + '@' + server.imap.hostname;
|
||||
app.tapCompose();
|
||||
app.typeTo(email);
|
||||
app.typeSubject('test email');
|
||||
app.typeBody('I still have a dream.');
|
||||
app.tapSend();
|
||||
}
|
||||
|
||||
function configureAndSend(messageCount) {
|
||||
|
||||
// Set up testy1, send email to testy1, since smtp fakserver
|
||||
// is paired with the imap fakserver for that account. So,
|
||||
// no cross sending of email across fakeserver instances.
|
||||
app.manualSetupImapEmail(server1);
|
||||
|
||||
for (var i = 0; i < messageCount; i++) {
|
||||
sendEmail(server1);
|
||||
}
|
||||
|
||||
// Now set up second account, to confirm system notifications
|
||||
// are only triggered in certain situations.
|
||||
app.tapFolderListButton();
|
||||
app.tapSettingsButton();
|
||||
app.tapAddAccountButton();
|
||||
app.manualSetupImapEmail(server2);
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
notification = new NotificationLib(client);
|
||||
sync = new EmailSync(client);
|
||||
sync.setup();
|
||||
|
||||
app.launch();
|
||||
});
|
||||
|
||||
test('should have 1 message notification in the different account',
|
||||
function() {
|
||||
configureAndSend(1);
|
||||
|
||||
sync.triggerSync();
|
||||
|
||||
assert(notification
|
||||
.getFirstNotificationData().data.type === 'message_reader');
|
||||
});
|
||||
|
||||
test('should have bulk message notification in the different account',
|
||||
function() {
|
||||
configureAndSend(2);
|
||||
|
||||
sync.triggerSync();
|
||||
|
||||
assert(notification
|
||||
.getFirstNotificationData().data.type === 'message_list');
|
||||
});
|
||||
|
||||
test('should not get a notification for same account', function() {
|
||||
configureAndSend(1);
|
||||
|
||||
// Switch back to testy1 account in the UI
|
||||
app.tapFolderListButton();
|
||||
app.tapAccountListButton();
|
||||
// switch to the testy1 account
|
||||
app.switchAccount(1);
|
||||
// hide the folder list page
|
||||
app.tapFolderListCloseButton();
|
||||
|
||||
// Now sync
|
||||
sync.triggerSync();
|
||||
|
||||
// Go back to system app
|
||||
client.switchToFrame();
|
||||
|
||||
notification.assertNoNotification();
|
||||
});
|
||||
});
|
||||
73
gaia/apps/email/test/marionette/notification_set_interval_test.js
Executable file
73
gaia/apps/email/test/marionette/notification_set_interval_test.js
Executable file
|
|
@ -0,0 +1,73 @@
|
|||
'use strict';
|
||||
/*jshint node: true */
|
||||
/*global marionette, setup, test */
|
||||
|
||||
var Email = require('./lib/email');
|
||||
var EmailData = require('./lib/email_data');
|
||||
var assert = require('assert');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
var SHARED_PATH = __dirname + '/../../../../shared/test/integration';
|
||||
|
||||
marionette('email notifications, set interval', function() {
|
||||
var app,
|
||||
client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
}),
|
||||
server1 = serverHelper.use({
|
||||
credentials: {
|
||||
username: 'testy1',
|
||||
password: 'testy1'
|
||||
}
|
||||
}, this);
|
||||
|
||||
|
||||
function getAlarms(client) {
|
||||
var alarms;
|
||||
client.waitFor(function() {
|
||||
alarms = client.executeScript(function() {
|
||||
var alarms = window.wrappedJSObject.navigator.__mozFakeAlarms;
|
||||
if (alarms && alarms.length) {
|
||||
return alarms;
|
||||
}
|
||||
});
|
||||
return alarms;
|
||||
});
|
||||
return alarms;
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
client.contentScript.inject(SHARED_PATH + '/mock_navigator_mozalarms.js');
|
||||
app.launch();
|
||||
});
|
||||
|
||||
test('should change sync interval from manual to 1 hour',
|
||||
function() {
|
||||
app.manualSetupImapEmail(server1);
|
||||
|
||||
// Open settings and change the sync interval value.
|
||||
app.tapFolderListButton();
|
||||
app.tapSettingsButton();
|
||||
app.tapSettingsAccountIndex(0);
|
||||
app.setSyncIntervalSelectValue(3600000);
|
||||
|
||||
// Wait for the account to report that it has the syncInterval
|
||||
// change.
|
||||
var emailData = new EmailData(client);
|
||||
emailData.waitForCurrentAccountUpdate('syncInterval', 3600000);
|
||||
|
||||
// Make sure an alarm was set.
|
||||
var alarms = getAlarms(client);
|
||||
assert(alarms.length === 1, 'have one alarm');
|
||||
var alarm = alarms[0];
|
||||
assert(alarm.interval === 3600000, 'has correct interval value');
|
||||
|
||||
// Close the app, relaunch and make sure syncInterval was
|
||||
// persisted correctly
|
||||
app.close();
|
||||
app.launch();
|
||||
var account = emailData.getCurrentAccount();
|
||||
assert(account.syncInterval === 3600000, 'sync interval is correct');
|
||||
});
|
||||
});
|
||||
60
gaia/apps/email/test/marionette/outbox_test.js
Executable file
60
gaia/apps/email/test/marionette/outbox_test.js
Executable file
|
|
@ -0,0 +1,60 @@
|
|||
'use strict';
|
||||
var Email = require('./lib/email');
|
||||
var assert = require('assert');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
/**
|
||||
* Other tests cover simple sending and receiving. Thus these outbox
|
||||
* tests focus on handling of the outbox itself -- messages pending in
|
||||
* the queue, refreshing them, etc.
|
||||
*/
|
||||
marionette('outbox', function() {
|
||||
var client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
|
||||
var INVALID_EMAIL = 'invalid@@';
|
||||
var app;
|
||||
var server = serverHelper.use(null, this);
|
||||
|
||||
suite('bad recipient', function() {
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
app.launch();
|
||||
app.manualSetupImapEmail(server);
|
||||
for (var i = 0; i < 2; i++) {
|
||||
app.tapCompose();
|
||||
app.typeTo(INVALID_EMAIL);
|
||||
app.tapSend();
|
||||
app.waitForToaster();
|
||||
}
|
||||
|
||||
// Navigate to the outbox.
|
||||
app.tapFolderListButton();
|
||||
app.tapOutboxItem();
|
||||
});
|
||||
|
||||
test('a failed message should end up in the outbox', function() {
|
||||
app.getHeaderAtIndex(0).tap();
|
||||
// This should take us to the compose screen, where we see the
|
||||
// outbox message, along with a warning telling us why the send
|
||||
// failed.
|
||||
assert.equal(app.getComposeTo(), INVALID_EMAIL);
|
||||
assert(app.getComposeErrorMessage().length > 0);
|
||||
});
|
||||
|
||||
test('refresh the outbox, message still fails', function() {
|
||||
assert.equal(app.getOutboxItemSyncIconForIndex(0), 'error');
|
||||
app.tapRefreshButton();
|
||||
// It should then finish syncing at some point; do to test timing
|
||||
// it would be unwise to try to test for the 'syncing' icon's
|
||||
// presence while refreshing, but we can at least check to ensure
|
||||
// the sync happened.
|
||||
client.waitFor(function() {
|
||||
return /just now/.test(app.getLastSyncText());
|
||||
});
|
||||
assert.equal(app.getOutboxItemSyncIconForIndex(0), 'error');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
69
gaia/apps/email/test/marionette/reply_imap_email_test.js
Executable file
69
gaia/apps/email/test/marionette/reply_imap_email_test.js
Executable file
|
|
@ -0,0 +1,69 @@
|
|||
'use strict';
|
||||
|
||||
var Email = require('./lib/email');
|
||||
var assert = require('assert');
|
||||
var format = require('util').format;
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
/**
|
||||
* Test that, upon receiving an e-mail, the user can click the "Reply"
|
||||
* menu, select among any of the available options ("Reply",
|
||||
* "Reply All", and "Forward", and that a compose window properly opens.
|
||||
* Checks for the appropriate text inside the compose window, such as
|
||||
* "John Doe wrote...", "Original message", etc., as appropriate.
|
||||
*/
|
||||
marionette('reply to an e-mail', function() {
|
||||
var client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
|
||||
var BODY_TEXT = 'I still have a dream.';
|
||||
|
||||
var app;
|
||||
var server = serverHelper.use(null, this);
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
app.launch();
|
||||
app.manualSetupImapEmail(server);
|
||||
app.sendAndReceiveMessages([
|
||||
{ to: 'testy@localhost', subject: 'test email', body: BODY_TEXT }
|
||||
]);
|
||||
app.tapEmailAtIndex(0);
|
||||
});
|
||||
|
||||
test('should be able to reply to an email', function() {
|
||||
app.tapReply('reply');
|
||||
var body = app.getComposeBody();
|
||||
assert(body.indexOf(BODY_TEXT) != -1,
|
||||
format('body should contain "%s", was "%s"', BODY_TEXT, body));
|
||||
assert(body.indexOf('wrote') != -1,
|
||||
format('body should contain "wrote", was "%s"', body));
|
||||
|
||||
app.abortCompose('message_reader');
|
||||
});
|
||||
|
||||
// Disabled because reply all should not actually work in this loopback
|
||||
// e-mail case; a bug related to forwarding was stopping us from disabling
|
||||
// reply-all.
|
||||
test.skip('should be able to "reply all" to an email', function() {
|
||||
app.tapReply('all');
|
||||
var body = app.getComposeBody();
|
||||
assert(body.indexOf(BODY_TEXT) != -1,
|
||||
format('body should contain "%s", was "%s"', BODY_TEXT, body));
|
||||
assert(body.indexOf('wrote') != -1,
|
||||
format('body should contain "wrote", was "%s"', body));
|
||||
|
||||
app.abortCompose('message_reader');
|
||||
});
|
||||
|
||||
test('should be able to forward an email', function() {
|
||||
app.tapReply('forward');
|
||||
var body = app.getComposeBody();
|
||||
assert(body.indexOf(BODY_TEXT) != -1,
|
||||
format('body should contain "%s", was "%s"', BODY_TEXT, body));
|
||||
assert(body.indexOf('Original message') != -1,
|
||||
format('body should contain "Original message", was "%s"', body));
|
||||
|
||||
app.abortCompose('message_reader');
|
||||
});
|
||||
});
|
||||
28
gaia/apps/email/test/marionette/search_test.js
Executable file
28
gaia/apps/email/test/marionette/search_test.js
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
var Email = require('./lib/email');
|
||||
var assert = require('assert');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
marionette('email message list edit mode', function() {
|
||||
var app;
|
||||
var client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
|
||||
var server = serverHelper.use(null, this);
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
app.launch();
|
||||
app.manualSetupImapEmail(server);
|
||||
});
|
||||
|
||||
test('only one input should appear', function() {
|
||||
app.tapSearchArea();
|
||||
|
||||
var inputs = app.getVisibleCardInputs();
|
||||
assert.equal(inputs.length, 1);
|
||||
});
|
||||
|
||||
});
|
||||
33
gaia/apps/email/test/marionette/send_imap_email_test.js
Executable file
33
gaia/apps/email/test/marionette/send_imap_email_test.js
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
'use strict';
|
||||
|
||||
var Email = require('./lib/email');
|
||||
var serverHelper = require('./lib/server_helper');
|
||||
|
||||
marionette('send email via IMAP', function() {
|
||||
var app,
|
||||
client = marionette.client({
|
||||
desiredCapabilities: { raisesAccessibilityExceptions: false }
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
app = new Email(client);
|
||||
app.launch();
|
||||
});
|
||||
|
||||
var server = serverHelper.use(null, this);
|
||||
|
||||
test('should send a email', function() {
|
||||
app.manualSetupImapEmail(server);
|
||||
app.tapCompose();
|
||||
|
||||
// write email to self
|
||||
app.typeTo('testy@localhost');
|
||||
app.typeSubject('test email');
|
||||
app.typeBody('I still have a dream.');
|
||||
app.tapSend();
|
||||
|
||||
app.tapRefreshButton();
|
||||
app.waitForSynchronized(0);
|
||||
});
|
||||
});
|
||||
|
||||
17
gaia/apps/email/test/unit/.jshintrc
Executable file
17
gaia/apps/email/test/unit/.jshintrc
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../../../../.jshintrc",
|
||||
"predef": [
|
||||
"assert",
|
||||
"suite",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"require",
|
||||
"requireApp",
|
||||
"sinon",
|
||||
"mocha",
|
||||
"loadBodyHTML"
|
||||
]
|
||||
}
|
||||
91
gaia/apps/email/test/unit/compose_card_test.js
Executable file
91
gaia/apps/email/test/unit/compose_card_test.js
Executable file
|
|
@ -0,0 +1,91 @@
|
|||
'use strict';
|
||||
/*global requireApp, suite, setup, testConfig, test, assert, suiteSetup,
|
||||
suiteTeardown */
|
||||
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
|
||||
suite('compose', function() {
|
||||
var subject, Compose;
|
||||
var mockAttachments = [{
|
||||
name: 'attachment1.jpg',
|
||||
blob: { size: 10000, type: 'type1' }
|
||||
}, {
|
||||
name: 'attachment2.jpg',
|
||||
blob: { size: 20000, type: 'type2' }
|
||||
}];
|
||||
|
||||
function MockComposer() {
|
||||
this.attachments = [];
|
||||
this.addAttachment = function(attachmentDef) {
|
||||
this.attachments.push({
|
||||
name: attachmentDef.name,
|
||||
blob: {
|
||||
size: attachmentDef.blob.size,
|
||||
type: attachmentDef.blob.type
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function testUpdateAttachmentsAriaLabel(expectedTotalSizeInKb) {
|
||||
subject.updateAttachmentsAriaLabel();
|
||||
assert.equal(subject.attachmentsContainer.getAttribute('data-l10n-id'),
|
||||
'compose-attachments-container');
|
||||
var args = subject.attachmentsContainer.getAttribute('data-l10n-args');
|
||||
assert.deepEqual(JSON.parse(args), {kilobytes: expectedTotalSizeInKb || 0});
|
||||
}
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig({
|
||||
suiteTeardown: suiteTeardown,
|
||||
done: done
|
||||
}, ['element!cards/compose'], function(c) {
|
||||
Compose = c;
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
subject = new Compose();
|
||||
subject.onArgs({ composer: new MockComposer() });
|
||||
});
|
||||
|
||||
suite('zero attachments', function() {
|
||||
test('calculateTotalAttachmentsSize', function() {
|
||||
assert.equal(subject.calculateTotalAttachmentsSize(), 0);
|
||||
});
|
||||
|
||||
test('updateAttachmentsAriaLabel', function() {
|
||||
testUpdateAttachmentsAriaLabel();
|
||||
});
|
||||
});
|
||||
|
||||
suite('one attachment', function() {
|
||||
setup(function() {
|
||||
subject.composer.addAttachment(mockAttachments[0]);
|
||||
});
|
||||
|
||||
test('calculateTotalAttachmentsSize', function() {
|
||||
assert.equal(subject.calculateTotalAttachmentsSize(), 10000);
|
||||
});
|
||||
|
||||
test('updateAttachmentsAriaLabel', function() {
|
||||
testUpdateAttachmentsAriaLabel();
|
||||
});
|
||||
});
|
||||
|
||||
suite('more than one attachment', function() {
|
||||
setup(function() {
|
||||
subject.composer.addAttachment(mockAttachments[0]);
|
||||
subject.composer.addAttachment(mockAttachments[1]);
|
||||
});
|
||||
|
||||
test('calculateTotalAttachmentsSize', function() {
|
||||
assert.equal(subject.calculateTotalAttachmentsSize(), 30000);
|
||||
});
|
||||
|
||||
test('updateAttachmentsAriaLabel', function() {
|
||||
testUpdateAttachmentsAriaLabel(Math.ceil(30000 / 1024));
|
||||
});
|
||||
});
|
||||
});
|
||||
293
gaia/apps/email/test/unit/evt_test.js
Executable file
293
gaia/apps/email/test/unit/evt_test.js
Executable file
|
|
@ -0,0 +1,293 @@
|
|||
/*jshint browser: true */
|
||||
/*global requireApp, suite, setup, testConfig, test, assert,
|
||||
suiteSetup, suiteTeardown */
|
||||
'use strict';
|
||||
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
|
||||
suite('evt', function() {
|
||||
var evt;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig(
|
||||
{
|
||||
suiteTeardown: suiteTeardown,
|
||||
done: done
|
||||
},
|
||||
['evt'],
|
||||
function(e) {
|
||||
evt = e;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
suite('#on', function() {
|
||||
|
||||
test('basic use', function() {
|
||||
var count = 0;
|
||||
|
||||
evt.on('testOn', function() {
|
||||
count += 1;
|
||||
});
|
||||
evt.emit('testOn');
|
||||
evt.emit('testOn');
|
||||
|
||||
//evt calls listeners synchronously, so should be complete now.
|
||||
assert.equal(count, 2);
|
||||
});
|
||||
|
||||
test('multiple args', function() {
|
||||
var count = 0;
|
||||
|
||||
evt.on('testOn', function(first, second) {
|
||||
count += 1;
|
||||
if (count === 1) {
|
||||
assert.equal(first, 'one');
|
||||
assert.equal(second, 'two');
|
||||
} else if (count === 2) {
|
||||
assert.equal(first, 'three');
|
||||
assert.equal(second, 'four');
|
||||
}
|
||||
});
|
||||
evt.emit('testOn', 'one', 'two');
|
||||
evt.emit('testOn', 'three', 'four');
|
||||
});
|
||||
|
||||
test('only catch second emit', function() {
|
||||
var count = 0;
|
||||
|
||||
evt.emit('testOn2');
|
||||
|
||||
evt.on('testOn2', function() {
|
||||
count += 1;
|
||||
});
|
||||
|
||||
evt.emit('testOn2');
|
||||
|
||||
assert.equal(count, 1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('#once', function() {
|
||||
|
||||
test('basic use', function() {
|
||||
var count = 0;
|
||||
|
||||
evt.once('testOnce', function() {
|
||||
count += 1;
|
||||
});
|
||||
evt.emit('testOnce');
|
||||
evt.emit('testOnce');
|
||||
assert.equal(count, 1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('#removeListener', function() {
|
||||
|
||||
test('basic use', function() {
|
||||
var count = 0;
|
||||
|
||||
function onEvent() {
|
||||
count += 1;
|
||||
evt.removeListener('testRemoveListener', onEvent);
|
||||
}
|
||||
|
||||
evt.on('testRemoveListener', onEvent);
|
||||
assert.equal(evt._events.hasOwnProperty('testRemoveListener'), true);
|
||||
|
||||
evt.emit('testRemoveListener');
|
||||
evt.emit('testRemoveListener');
|
||||
|
||||
assert.equal(count, 1);
|
||||
assert.equal(evt._events.hasOwnProperty('testRemoveListener'), false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
suite('#emitWhenListener', function() {
|
||||
|
||||
test('basic use', function() {
|
||||
var count = 0;
|
||||
|
||||
evt.emitWhenListener('testEmitWhenListener');
|
||||
|
||||
evt.on('testEmitWhenListener', function() {
|
||||
count += 1;
|
||||
});
|
||||
|
||||
evt.emit('testEmitWhenListener');
|
||||
assert.equal(count, 2);
|
||||
});
|
||||
});
|
||||
|
||||
suite('#latest', function() {
|
||||
var data;
|
||||
|
||||
// Executed for each test() below
|
||||
setup(function() {
|
||||
data = {
|
||||
setToken: function(value) {
|
||||
this.token = value;
|
||||
this.emitWhenListener('token', this.token);
|
||||
},
|
||||
token: null
|
||||
};
|
||||
evt.mix(data);
|
||||
});
|
||||
|
||||
test('delayed value', function() {
|
||||
var count = 0;
|
||||
|
||||
data.latest('token', function(token) {
|
||||
count += 1;
|
||||
|
||||
if (count === 1) {
|
||||
assert.equal(token, 'one');
|
||||
} else if (count === 2) {
|
||||
assert.equal(token, 'two');
|
||||
}
|
||||
});
|
||||
|
||||
data.setToken('one');
|
||||
data.setToken('two');
|
||||
|
||||
assert.equal(count, 2);
|
||||
});
|
||||
|
||||
test('immediate value', function() {
|
||||
var count = 0;
|
||||
|
||||
data.setToken('one');
|
||||
|
||||
data.latest('token', function(token) {
|
||||
count += 1;
|
||||
|
||||
if (count === 1) {
|
||||
assert.equal(token, 'one');
|
||||
} else if (count === 2) {
|
||||
assert.equal(token, 'two');
|
||||
}
|
||||
});
|
||||
|
||||
assert.equal(count, 1);
|
||||
|
||||
data.setToken('two');
|
||||
|
||||
assert.equal(count, 2);
|
||||
});
|
||||
|
||||
test('immediate value, after first listener', function() {
|
||||
var count = 0;
|
||||
|
||||
data.setToken('one');
|
||||
|
||||
data.latest('token', function(token) {
|
||||
count += 1;
|
||||
|
||||
assert.equal(token, 'one');
|
||||
|
||||
// Second latest, which should trigger the callback
|
||||
// even though there is no pending emit.
|
||||
data.latest('token', function(token) {
|
||||
count += 1;
|
||||
|
||||
assert.equal(token, 'one');
|
||||
assert.equal(count, 2);
|
||||
});
|
||||
});
|
||||
|
||||
assert.equal(count, 2);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('#latestOnce', function() {
|
||||
var data;
|
||||
|
||||
// Executed for each test() below
|
||||
setup(function() {
|
||||
data = {
|
||||
setToken: function(value) {
|
||||
this.token = value;
|
||||
this.emitWhenListener('token', this.token);
|
||||
},
|
||||
token: null
|
||||
};
|
||||
evt.mix(data);
|
||||
});
|
||||
|
||||
|
||||
test('double latestOnce listeners', function() {
|
||||
var count = 0;
|
||||
|
||||
function onLatest(token) {
|
||||
count += 1;
|
||||
|
||||
assert.equal(token, 'one');
|
||||
}
|
||||
|
||||
data.latestOnce('token', onLatest);
|
||||
data.latestOnce('token', onLatest);
|
||||
|
||||
data.setToken('one');
|
||||
assert.equal(count, 2);
|
||||
});
|
||||
|
||||
test('delayed value', function() {
|
||||
var count = 0;
|
||||
|
||||
data.latestOnce('token', function(token) {
|
||||
count += 1;
|
||||
|
||||
assert.equal(token, 'one');
|
||||
});
|
||||
|
||||
data.setToken('one');
|
||||
data.setToken('two');
|
||||
assert.equal(count, 1);
|
||||
});
|
||||
|
||||
test('immediate value', function() {
|
||||
var count = 0;
|
||||
|
||||
data.setToken('one');
|
||||
|
||||
data.latestOnce('token', function(token) {
|
||||
count += 1;
|
||||
|
||||
assert.equal(token, 'one');
|
||||
});
|
||||
|
||||
assert.equal(count, 1);
|
||||
data.setToken('two');
|
||||
assert.equal(count, 1);
|
||||
});
|
||||
|
||||
test('immediate value, after first listener', function() {
|
||||
var count = 0;
|
||||
|
||||
data.setToken('one');
|
||||
|
||||
data.latestOnce('token', function(token) {
|
||||
count += 1;
|
||||
|
||||
assert.equal(token, 'one');
|
||||
|
||||
// Second latestOnce, which should trigger the callback
|
||||
// even though there is no pending emit.
|
||||
data.latestOnce('token', function(token) {
|
||||
count += 1;
|
||||
assert.equal(token, 'one');
|
||||
});
|
||||
});
|
||||
|
||||
assert.equal(count, 2);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
112
gaia/apps/email/test/unit/header_cursor_test.js
Executable file
112
gaia/apps/email/test/unit/header_cursor_test.js
Executable file
|
|
@ -0,0 +1,112 @@
|
|||
'use strict';
|
||||
/* global testConfig */
|
||||
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
|
||||
/**
|
||||
|
||||
* Fake MailHeader instances for our cursor's messagesSlice.
|
||||
*/
|
||||
var MESSAGE_ONE = Object.freeze({ id: 'one' }),
|
||||
MESSAGE_TWO = Object.freeze({ id: 'two' }),
|
||||
MESSAGE_THREE = Object.freeze({ id: 'three' });
|
||||
|
||||
suite('HeaderCursor', function() {
|
||||
var evt, headerCursor, CurrentMessage;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig({ done: done }, [
|
||||
'evt',
|
||||
'header_cursor',
|
||||
'model_create',
|
||||
], function(_evt, HeaderCursor, mc) {
|
||||
evt = _evt;
|
||||
headerCursor = new HeaderCursor(mc.defaultModel);
|
||||
CurrentMessage = HeaderCursor.CurrentMessage;
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
headerCursor.messagesSlice = {
|
||||
die: function() {},
|
||||
items: [
|
||||
MESSAGE_ONE,
|
||||
MESSAGE_TWO,
|
||||
MESSAGE_THREE
|
||||
]
|
||||
};
|
||||
|
||||
headerCursor.currentMessage = new CurrentMessage(
|
||||
MESSAGE_TWO, {
|
||||
hasPrevious: true,
|
||||
hasNext: true
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
suite('#advance', function() {
|
||||
test('next should go to next', function(done) {
|
||||
headerCursor.once('currentMessage', function(currentMessage) {
|
||||
assert.deepEqual(
|
||||
currentMessage,
|
||||
new CurrentMessage(MESSAGE_THREE, {
|
||||
hasPrevious: true,
|
||||
hasNext: false
|
||||
})
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
headerCursor.advance('next');
|
||||
});
|
||||
|
||||
test('previous should go to previous', function(done) {
|
||||
headerCursor.once('currentMessage', function(currentMessage) {
|
||||
assert.deepEqual(
|
||||
currentMessage,
|
||||
new CurrentMessage(MESSAGE_ONE, {
|
||||
hasPrevious: false,
|
||||
hasNext: true
|
||||
})
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
headerCursor.advance('previous');
|
||||
});
|
||||
|
||||
test('should not die if advance out of bounds', function() {
|
||||
headerCursor.currentMessage = new CurrentMessage(
|
||||
MESSAGE_THREE, {
|
||||
hasPrevious: true,
|
||||
hasNext: false
|
||||
}
|
||||
);
|
||||
|
||||
// If this doesn't error, life is good!
|
||||
headerCursor.advance('next');
|
||||
});
|
||||
});
|
||||
|
||||
suite('#indexOfMessageById', function() {
|
||||
test('should be correct if contains message with id', function() {
|
||||
assert.ok(headerCursor.indexOfMessageById('two'), 2);
|
||||
});
|
||||
|
||||
test('should be -1 if not contains message with id', function() {
|
||||
assert.ok(headerCursor.indexOfMessageById('purple'), -1);
|
||||
});
|
||||
});
|
||||
|
||||
suite('#die', function() {
|
||||
test('should die messagesSlice', function() {
|
||||
var die = sinon.stub(headerCursor.messagesSlice, 'die');
|
||||
headerCursor.die();
|
||||
sinon.assert.called(die);
|
||||
assert.strictEqual(headerCursor.messagesSlice, null);
|
||||
});
|
||||
});
|
||||
});
|
||||
126
gaia/apps/email/test/unit/mail_app_test.js
Executable file
126
gaia/apps/email/test/unit/mail_app_test.js
Executable file
|
|
@ -0,0 +1,126 @@
|
|||
'use strict';
|
||||
/*global requireApp, suite, suiteSetup, testConfig, test,
|
||||
assert, suiteTeardown */
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
|
||||
|
||||
suite('email/query_uri', function() {
|
||||
var queryURI;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig(
|
||||
{
|
||||
suiteTeardown: suiteTeardown,
|
||||
done: done
|
||||
},
|
||||
['query_uri'],
|
||||
function(quri) {
|
||||
queryURI = quri;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('#to', function() {
|
||||
|
||||
assert.deepEqual(queryURI('mailto:Email.address1@mailto.com'),
|
||||
{ to: ['Email.address1@mailto.com'] },
|
||||
'to single address test fail');
|
||||
|
||||
assert.deepEqual(queryURI(
|
||||
'mailto:Email.address1@mailto.com;Email.address2@mailto.com'),
|
||||
{ to: ['Email.address1@mailto.com', 'Email.address2@mailto.com'] },
|
||||
'to multi-addresses test fail (separator ";")');
|
||||
|
||||
assert.deepEqual(queryURI(
|
||||
'mailto:Email.address1@mailto.com,Email.address2@mailto.com'),
|
||||
{ to: ['Email.address1@mailto.com', 'Email.address2@mailto.com'] },
|
||||
'to multi-addresses test fail (separator ",")');
|
||||
|
||||
assert.deepEqual(queryURI('mailto:'),
|
||||
{ to: [] },
|
||||
'no to address test fail');
|
||||
|
||||
});
|
||||
|
||||
test('#cc', function() {
|
||||
|
||||
assert.deepEqual(queryURI('mailto:?cc=EmailCc.address@mailto.com'),
|
||||
{ to: [], cc: ['EmailCc.address@mailto.com'] },
|
||||
'cc single address test fail');
|
||||
|
||||
assert.deepEqual(queryURI('mailto:?cc=EmailCc.address1@mailto.com;' +
|
||||
'EmailCc.address2@mailto.com;EmailCc.address3@mailto.com'),
|
||||
{ to: [], cc: ['EmailCc.address1@mailto.com', 'EmailCc.address2@mailto.com',
|
||||
'EmailCc.address3@mailto.com'] },
|
||||
'cc multi-addresses test fail');
|
||||
|
||||
});
|
||||
|
||||
|
||||
test('#bcc', function() {
|
||||
|
||||
assert.deepEqual(queryURI('mailto:?bcc=EmailBcc.address@mailto.com'),
|
||||
{ to: [], bcc: ['EmailBcc.address@mailto.com'] },
|
||||
'bcc single address test');
|
||||
|
||||
assert.deepEqual(queryURI('mailto:?bcc=EmailBcc.address1@mailto.com;' +
|
||||
'EmailBcc.address2@mailto.com;EmailBcc.address3@mailto.com'),
|
||||
{ to: [], bcc: ['EmailBcc.address1@mailto.com',
|
||||
'EmailBcc.address2@mailto.com', 'EmailBcc.address3@mailto.com'] },
|
||||
'bcc multi-addresses test fail');
|
||||
|
||||
});
|
||||
|
||||
|
||||
test('#subject', function() {
|
||||
|
||||
assert.deepEqual(queryURI('mailto:?subject=This is the subject line'),
|
||||
{ to: [], subject: 'This is the subject line' },
|
||||
'subject test fail');
|
||||
|
||||
});
|
||||
|
||||
|
||||
test('#body', function() {
|
||||
|
||||
assert.deepEqual(queryURI('mailto:?body=This is the body'),
|
||||
{ to: [], body: 'This is the body' },
|
||||
'body test fail');
|
||||
|
||||
});
|
||||
|
||||
|
||||
test('#complex tests', function() {
|
||||
|
||||
assert.deepEqual(queryURI(
|
||||
'mailto:Email.address1@mailto.com;Email.address2@mailto.com?' +
|
||||
'cc=EmailCc1.address@mailto.com;EmailCc2.address@mailto.com&' +
|
||||
'bcc=EmailBCc1.address@mailto.com;EmailBCc2.address@mailto.com'),
|
||||
{
|
||||
to: ['Email.address1@mailto.com', 'Email.address2@mailto.com'],
|
||||
cc: ['EmailCc1.address@mailto.com', 'EmailCc2.address@mailto.com'],
|
||||
bcc: ['EmailBCc1.address@mailto.com', 'EmailBCc2.address@mailto.com']
|
||||
},
|
||||
'complex 1 test fail');
|
||||
|
||||
|
||||
assert.deepEqual(queryURI('mailto:Email.address1@mailto.com;?' +
|
||||
'cc=EmailCc1.address@mailto.com;&bcc=EmailBCc1.address@mailto.com;' +
|
||||
'&subject=This is the subject line&body=This is the text line one.' +
|
||||
' %0AThis is the text line two'),
|
||||
|
||||
{
|
||||
to: ['Email.address1@mailto.com'],
|
||||
subject: 'This is the subject line',
|
||||
body: 'This is the text line one. \nThis is the text line two',
|
||||
cc: ['EmailCc1.address@mailto.com'],
|
||||
bcc: ['EmailBCc1.address@mailto.com']
|
||||
},
|
||||
|
||||
'complex 2 test fail');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
205
gaia/apps/email/test/unit/message_list_search_card_test.js
Executable file
205
gaia/apps/email/test/unit/message_list_search_card_test.js
Executable file
|
|
@ -0,0 +1,205 @@
|
|||
'use strict';
|
||||
/*global requireApp, suite, setup, testConfig, test, assert, suiteSetup,
|
||||
suiteTeardown */
|
||||
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
|
||||
suite('message_list', function() {
|
||||
var subject, modelCreate, HeaderCursor, MessageList;
|
||||
var mockMessagesSlice = { items: [], die: function() {} };
|
||||
var mockStarredMessage = {
|
||||
isStarred: true
|
||||
};
|
||||
var mockReadMessage = {
|
||||
isRead: true
|
||||
};
|
||||
var mockStarredReadMessage = {
|
||||
isStarred: true,
|
||||
isRead: true
|
||||
};
|
||||
var mockMessage = {
|
||||
author: { name: 'author' },
|
||||
subject: 'subject',
|
||||
snippet: 'this is a body snippet',
|
||||
date: Date.now(),
|
||||
sendStatus: {}
|
||||
};
|
||||
var mockMatches = { author: { text: 'auth', matchRuns: [] } };
|
||||
|
||||
function testSelectedMessage(element, editMode, selected) {
|
||||
assert.equal(element.getAttribute('aria-selected'),
|
||||
editMode ? selected ? 'true' : 'false' : null);
|
||||
assert.equal(element.querySelector('input[type=checkbox]').checked,
|
||||
editMode && selected);
|
||||
}
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig({
|
||||
suiteTeardown: suiteTeardown,
|
||||
done: done
|
||||
}, [
|
||||
'model_create',
|
||||
'header_cursor',
|
||||
'element!cards/message_list_search',
|
||||
'tmpl!cards/msg/header_item.html'], function(mc, hc, ml, hi) {
|
||||
modelCreate = mc;
|
||||
HeaderCursor = hc;
|
||||
mockMessage.element = hi.cloneNode(true);
|
||||
MessageList = ml;
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
var headerCursor = new HeaderCursor(modelCreate.defaultModel);
|
||||
headerCursor.messagesSlice = mockMessagesSlice;
|
||||
subject = new MessageList();
|
||||
subject.onArgs({
|
||||
model: modelCreate.defaultModel,
|
||||
headerCursor: headerCursor
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateDomEditControls', function() {
|
||||
test('no selected messages', function() {
|
||||
subject.selectedMessages = [];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-star-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-read-button');
|
||||
});
|
||||
|
||||
test('one read message', function() {
|
||||
subject.selectedMessages = [Object.create(mockReadMessage)];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-star-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-unread-button');
|
||||
});
|
||||
|
||||
test('one starred message', function() {
|
||||
subject.selectedMessages = [Object.create(mockStarredMessage)];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-unstar-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-read-button');
|
||||
});
|
||||
|
||||
test('one read and starred message', function() {
|
||||
subject.selectedMessages = [Object.create(mockStarredReadMessage)];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-unstar-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-unread-button');
|
||||
});
|
||||
|
||||
test('one read and one unread message', function() {
|
||||
subject.selectedMessages = [Object.create(mockReadMessage),
|
||||
Object.create(mockMessage)];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-star-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-unread-button');
|
||||
});
|
||||
|
||||
test('one starred and one unstarred message', function() {
|
||||
subject.selectedMessages = [Object.create(mockStarredMessage),
|
||||
Object.create(mockMessage)];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-star-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-read-button');
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateDomMessageChecked', function() {
|
||||
var element;
|
||||
setup(function() {
|
||||
element = Object.create(mockMessage).element;
|
||||
});
|
||||
|
||||
test('set checked', function() {
|
||||
subject.updateDomMessageChecked(element, true);
|
||||
testSelectedMessage(element, true, true);
|
||||
});
|
||||
|
||||
test('set not checked', function() {
|
||||
subject.updateDomMessageChecked(element, false);
|
||||
testSelectedMessage(element, true, false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateDomSelectState', function() {
|
||||
var message, element;
|
||||
setup(function() {
|
||||
message = Object.create(mockMessage);
|
||||
element = message.element;
|
||||
subject.selectedMessages = [];
|
||||
subject.editMode = true;
|
||||
});
|
||||
|
||||
test('not in edit mode', function() {
|
||||
subject.editMode = false;
|
||||
subject.updateDomSelectState(element, message);
|
||||
testSelectedMessage(element, false);
|
||||
});
|
||||
|
||||
test('set selected', function() {
|
||||
subject.selectedMessages.push(message);
|
||||
subject.updateDomSelectState(element, message);
|
||||
testSelectedMessage(element, true, true);
|
||||
});
|
||||
|
||||
test('set not selected', function() {
|
||||
subject.selectedMessages.pop(message);
|
||||
subject.updateDomSelectState(element, message);
|
||||
testSelectedMessage(element, true, false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateMatchedMessageDom', function() {
|
||||
var message, element;
|
||||
setup(function() {
|
||||
message = Object.create(mockMessage);
|
||||
element = message.element;
|
||||
subject.editMode = true;
|
||||
subject.selectedMessages = [];
|
||||
});
|
||||
|
||||
test('not edit mode', function() {
|
||||
subject.editMode = false;
|
||||
subject.updateMatchedMessageDom({
|
||||
element: element,
|
||||
header: message,
|
||||
matches: mockMatches
|
||||
});
|
||||
testSelectedMessage(element, false);
|
||||
});
|
||||
|
||||
test('message selected', function() {
|
||||
subject.selectedMessages.push(message);
|
||||
subject.updateMatchedMessageDom({
|
||||
element: element,
|
||||
header: message,
|
||||
matches: mockMatches
|
||||
});
|
||||
testSelectedMessage(element, true, true);
|
||||
});
|
||||
|
||||
test('message not selected', function() {
|
||||
subject.selectedMessages.pop(message);
|
||||
subject.updateMatchedMessageDom({
|
||||
element: element,
|
||||
header: message,
|
||||
matches: mockMatches
|
||||
});
|
||||
testSelectedMessage(element, true, false);
|
||||
});
|
||||
});
|
||||
});
|
||||
162
gaia/apps/email/test/unit/message_reader_card_test.js
Executable file
162
gaia/apps/email/test/unit/message_reader_card_test.js
Executable file
|
|
@ -0,0 +1,162 @@
|
|||
'use strict';
|
||||
/*global requireApp, suite, setup, testConfig, test, assert, suiteSetup,
|
||||
suiteTeardown */
|
||||
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
|
||||
suite('message_reader', function() {
|
||||
var subject, MessageReader, evt;
|
||||
function MockMailHeader() {}
|
||||
MockMailHeader.prototype = {
|
||||
makeCopy: function() {
|
||||
var copy = new MockMailHeader();
|
||||
copy.isRead = this.isRead;
|
||||
copy.isStarred = this.isStarred;
|
||||
return copy;
|
||||
},
|
||||
setRead: function(isRead) { this.isRead = isRead; },
|
||||
isRead: false,
|
||||
isStarred: false,
|
||||
setStarred: function() {}
|
||||
};
|
||||
var mockBody = {
|
||||
bodyReps: [],
|
||||
attachments: [{
|
||||
isDownloaded: false,
|
||||
isDownloadable: true,
|
||||
sizeEstimateInBytes: 1024,
|
||||
mimetype: '',
|
||||
filename: ''
|
||||
}]
|
||||
};
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig({
|
||||
suiteTeardown: suiteTeardown,
|
||||
done: done
|
||||
}, ['evt', 'element!cards/message_reader'], function(e, mr) {
|
||||
evt = e;
|
||||
MessageReader = mr;
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
subject = new MessageReader();
|
||||
});
|
||||
|
||||
suite('_setHeader', function() {
|
||||
var header;
|
||||
setup(function() {
|
||||
header = new MockMailHeader();
|
||||
subject.readBtn.removeAttribute('data-l10n-id');
|
||||
});
|
||||
|
||||
test('isRead', function() {
|
||||
header.isRead = true;
|
||||
subject._setHeader(header);
|
||||
assert.equal(subject.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-read-button');
|
||||
});
|
||||
|
||||
test('!isRead', function() {
|
||||
subject._setHeader(header);
|
||||
assert.isNull(subject.readBtn.getAttribute('data-l10n-id'));
|
||||
});
|
||||
|
||||
test('isStarred', function() {
|
||||
header.isStarred = true;
|
||||
subject._setHeader(header);
|
||||
assert.equal(subject.starBtn.getAttribute('aria-pressed'), 'true');
|
||||
});
|
||||
|
||||
test('!isStarred', function() {
|
||||
subject._setHeader(header);
|
||||
assert.equal(subject.starBtn.getAttribute('aria-pressed'), 'false');
|
||||
});
|
||||
});
|
||||
|
||||
suite('onToggleStar', function() {
|
||||
setup(function() {
|
||||
subject.hackMutationHeader = new MockMailHeader();
|
||||
subject.header = new MockMailHeader();
|
||||
});
|
||||
|
||||
test('isStarred', function() {
|
||||
subject.onToggleStar();
|
||||
assert.isTrue(subject.starBtn.classList.contains('msg-star-btn-on'));
|
||||
assert.equal(subject.starBtn.getAttribute('aria-pressed'), 'true');
|
||||
});
|
||||
|
||||
test('!isStarred', function() {
|
||||
subject.hackMutationHeader.isStarred = true;
|
||||
subject.onToggleStar();
|
||||
assert.isFalse(subject.starBtn.classList.contains('msg-star-btn-on'));
|
||||
assert.equal(subject.starBtn.getAttribute('aria-pressed'), 'false');
|
||||
});
|
||||
});
|
||||
|
||||
suite('setRead', function() {
|
||||
setup(function() {
|
||||
subject.hackMutationHeader = new MockMailHeader();
|
||||
subject.header = new MockMailHeader();
|
||||
});
|
||||
|
||||
test('isRead', function() {
|
||||
subject.setRead(true);
|
||||
assert.equal(subject.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-read-button');
|
||||
});
|
||||
|
||||
test('!isRead', function() {
|
||||
subject.setRead(false);
|
||||
assert.equal(subject.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-unread-button');
|
||||
});
|
||||
});
|
||||
|
||||
suite('clearDom', function() {
|
||||
var mockMessageContent = '<div>Message content</div>';
|
||||
setup(function() {
|
||||
subject.rootBodyNode.innerHTML = mockMessageContent;
|
||||
assert.equal(subject.rootBodyNode.innerHTML, mockMessageContent);
|
||||
assert.isNull(subject.rootBodyNode.querySelector('progress'));
|
||||
});
|
||||
|
||||
test('test progress l10n', function() {
|
||||
subject.clearDom();
|
||||
var progress = subject.rootBodyNode.querySelector('progress');
|
||||
assert.ok(progress);
|
||||
assert.equal(progress.getAttribute('data-l10n-id'),
|
||||
'message-body-container-progress');
|
||||
});
|
||||
});
|
||||
|
||||
suite('buildBodyDom', function() {
|
||||
setup(function() {
|
||||
subject.body = mockBody;
|
||||
subject.header = new MockMailHeader();
|
||||
});
|
||||
|
||||
test('disabled attachments accessibility', function(done) {
|
||||
subject.body.attachments[0].isDownloadable = false;
|
||||
evt.once('metrics:contentDone', function() {
|
||||
assert.equal(subject.attachmentsContainer.children[0].getAttribute(
|
||||
'aria-disabled'), 'true');
|
||||
done();
|
||||
});
|
||||
subject.buildBodyDom();
|
||||
});
|
||||
|
||||
test('attachments accessibility', function(done) {
|
||||
subject.body.attachments[0].isDownloadable = true;
|
||||
subject.body.attachments[0].isDownloaded = true;
|
||||
evt.once('metrics:contentDone', function() {
|
||||
assert.equal(subject.attachmentsContainer.children[0].getAttribute(
|
||||
'aria-disabled'), 'false');
|
||||
done();
|
||||
});
|
||||
subject.buildBodyDom();
|
||||
});
|
||||
});
|
||||
});
|
||||
283
gaia/apps/email/test/unit/messsage_list_card_test.js
Executable file
283
gaia/apps/email/test/unit/messsage_list_card_test.js
Executable file
|
|
@ -0,0 +1,283 @@
|
|||
'use strict';
|
||||
/*global requireApp, suite, setup, testConfig, test, assert, suiteSetup,
|
||||
suiteTeardown */
|
||||
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
|
||||
suite('message_list', function() {
|
||||
var subject, modelCreate, HeaderCursor, MessageList;
|
||||
var mockMessagesSlice = { items: [], die: function() {} };
|
||||
var mockStarredMessage = {
|
||||
isStarred: true
|
||||
};
|
||||
var mockReadMessage = {
|
||||
isRead: true
|
||||
};
|
||||
var mockStarredReadMessage = {
|
||||
isStarred: true,
|
||||
isRead: true
|
||||
};
|
||||
var mockMessage = {
|
||||
author: { name: 'author' },
|
||||
subject: 'subject',
|
||||
snippet: 'this is a body snippet',
|
||||
date: Date.now(),
|
||||
sendStatus: {}
|
||||
};
|
||||
|
||||
function testRefreshBtnAccessibility(syncing) {
|
||||
assert.equal(subject.refreshBtn.getAttribute('data-l10n-id'),
|
||||
syncing ? 'messages-refresh-progress' : 'messages-refresh-button');
|
||||
if (syncing) {
|
||||
assert.equal(subject.refreshBtn.getAttribute('role'), 'progressbar');
|
||||
} else {
|
||||
assert.isNull(subject.refreshBtn.getAttribute('role'));
|
||||
}
|
||||
}
|
||||
|
||||
function testSelectedMessage(element, editMode, selected) {
|
||||
assert.equal(element.getAttribute('aria-selected'),
|
||||
editMode ? selected ? 'true' : 'false' : null);
|
||||
assert.equal(element.querySelector('input[type=checkbox]').checked,
|
||||
editMode && selected);
|
||||
}
|
||||
|
||||
function testMessageState(message, state) {
|
||||
message.sendStatus.state = state;
|
||||
subject.updateMessageDom(message);
|
||||
var syncingNode = message.element
|
||||
.querySelector('.msg-header-syncing-section');
|
||||
if (state) {
|
||||
assert.equal(syncingNode.getAttribute('data-l10n-id'),
|
||||
'message-header-state-' + state);
|
||||
} else {
|
||||
assert.equal(true, !syncingNode.hasAttribute('data-l10n-id'));
|
||||
}
|
||||
}
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig({
|
||||
suiteTeardown: suiteTeardown,
|
||||
done: done
|
||||
}, [
|
||||
'model_create',
|
||||
'header_cursor',
|
||||
'element!cards/message_list',
|
||||
'tmpl!cards/msg/header_item.html'], function(mc, hc, ml, hi) {
|
||||
modelCreate = mc;
|
||||
HeaderCursor = hc;
|
||||
mockMessage.element = hi.cloneNode(true);
|
||||
MessageList = ml;
|
||||
});
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
var headerCursor = new HeaderCursor(modelCreate.defaultModel);
|
||||
headerCursor.messagesSlice = mockMessagesSlice;
|
||||
subject = new MessageList();
|
||||
subject.onArgs({
|
||||
model: modelCreate.defaultModel,
|
||||
headerCursor: headerCursor
|
||||
});
|
||||
});
|
||||
|
||||
suite('messages_status', function() {
|
||||
setup(function() {
|
||||
subject.curFolder = { type: 'inbox' };
|
||||
});
|
||||
|
||||
test('synchronizing', function() {
|
||||
testRefreshBtnAccessibility(false);
|
||||
subject.msgVScroll.messages_status('synchronizing');
|
||||
testRefreshBtnAccessibility(true);
|
||||
});
|
||||
|
||||
test('synced', function() {
|
||||
testRefreshBtnAccessibility(false);
|
||||
subject.msgVScroll.messages_status('synced');
|
||||
testRefreshBtnAccessibility(false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('toggleOutboxSyncingDisplay', function() {
|
||||
setup(function() {
|
||||
subject.curFolder = { type: 'outbox' };
|
||||
});
|
||||
|
||||
test('syncing', function() {
|
||||
testRefreshBtnAccessibility(false);
|
||||
subject.toggleOutboxSyncingDisplay(true);
|
||||
testRefreshBtnAccessibility(true);
|
||||
});
|
||||
|
||||
test('not syncing', function() {
|
||||
setup(function() {
|
||||
subject._outboxSyncing = true;
|
||||
});
|
||||
|
||||
testRefreshBtnAccessibility(false);
|
||||
subject.toggleOutboxSyncingDisplay(false);
|
||||
testRefreshBtnAccessibility(false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateDomEditControls', function() {
|
||||
test('no selected messages', function() {
|
||||
subject.selectedMessages = [];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-star-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-read-button');
|
||||
});
|
||||
|
||||
test('one read message', function() {
|
||||
subject.selectedMessages = [Object.create(mockReadMessage)];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-star-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-unread-button');
|
||||
});
|
||||
|
||||
test('one starred message', function() {
|
||||
subject.selectedMessages = [Object.create(mockStarredMessage)];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-unstar-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-read-button');
|
||||
});
|
||||
|
||||
test('one read and starred message', function() {
|
||||
subject.selectedMessages = [Object.create(mockStarredReadMessage)];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-unstar-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-unread-button');
|
||||
});
|
||||
|
||||
test('one read and one unread message', function() {
|
||||
subject.selectedMessages = [Object.create(mockReadMessage),
|
||||
Object.create(mockMessage)];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-star-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-unread-button');
|
||||
});
|
||||
|
||||
test('one starred and one unstarred message', function() {
|
||||
subject.selectedMessages = [Object.create(mockStarredMessage),
|
||||
Object.create(mockMessage)];
|
||||
subject.updateDomEditControls();
|
||||
assert.equal(subject.editToolbar.starBtn.getAttribute('data-l10n-id'),
|
||||
'message-star-button');
|
||||
assert.equal(subject.editToolbar.readBtn.getAttribute('data-l10n-id'),
|
||||
'message-mark-read-button');
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateDomMessageChecked', function() {
|
||||
var element;
|
||||
setup(function() {
|
||||
element = Object.create(mockMessage).element;
|
||||
});
|
||||
|
||||
test('set checked', function() {
|
||||
subject.updateDomMessageChecked(element, true);
|
||||
testSelectedMessage(element, true, true);
|
||||
});
|
||||
|
||||
test('set not checked', function() {
|
||||
subject.updateDomMessageChecked(element, false);
|
||||
testSelectedMessage(element, true, false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateDomSelectState', function() {
|
||||
var message, element;
|
||||
setup(function() {
|
||||
message = Object.create(mockMessage);
|
||||
element = message.element;
|
||||
subject.selectedMessages = [];
|
||||
subject.editMode = true;
|
||||
});
|
||||
|
||||
test('not in edit mode', function() {
|
||||
subject.editMode = false;
|
||||
subject.updateDomSelectState(element, message);
|
||||
testSelectedMessage(element, false);
|
||||
});
|
||||
|
||||
test('set selected', function() {
|
||||
subject.selectedMessages.push(message);
|
||||
subject.updateDomSelectState(element, message);
|
||||
testSelectedMessage(element, true, true);
|
||||
});
|
||||
|
||||
test('set not selected', function() {
|
||||
subject.selectedMessages.pop(message);
|
||||
subject.updateDomSelectState(element, message);
|
||||
testSelectedMessage(element, true, false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('_setEditMode', function() {
|
||||
var element;
|
||||
setup(function() {
|
||||
var message = Object.create(mockMessage);
|
||||
subject.updateMessageDom(message);
|
||||
element = message.element;
|
||||
assert.isNull(element.getAttribute('aria-selected'));
|
||||
subject.msgVScroll.appendChild(element);
|
||||
});
|
||||
|
||||
test('in editMode', function() {
|
||||
subject._setEditMode(true);
|
||||
testSelectedMessage(element, true, false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('updateMessageDom', function() {
|
||||
var message, element;
|
||||
setup(function() {
|
||||
message = Object.create(mockMessage);
|
||||
element = message.element;
|
||||
subject.editMode = true;
|
||||
subject.selectedMessages = [];
|
||||
});
|
||||
|
||||
test('not edit mode', function() {
|
||||
subject.editMode = false;
|
||||
subject.updateMessageDom(message);
|
||||
testSelectedMessage(element, false);
|
||||
});
|
||||
|
||||
test('message selected', function() {
|
||||
subject.selectedMessages.push(message);
|
||||
subject.updateMessageDom(message);
|
||||
testSelectedMessage(element, true, true);
|
||||
});
|
||||
|
||||
test('message not selected', function() {
|
||||
subject.selectedMessages.pop(message);
|
||||
subject.updateMessageDom(message);
|
||||
testSelectedMessage(element, true, false);
|
||||
});
|
||||
|
||||
test('message has a sending state', function() {
|
||||
testMessageState(message, 'sending');
|
||||
});
|
||||
|
||||
test('message has an error state', function() {
|
||||
testMessageState(message, 'error');
|
||||
});
|
||||
|
||||
test('message has no state', function() {
|
||||
testMessageState(message);
|
||||
});
|
||||
});
|
||||
});
|
||||
82
gaia/apps/email/test/unit/mock_api.js
Executable file
82
gaia/apps/email/test/unit/mock_api.js
Executable file
|
|
@ -0,0 +1,82 @@
|
|||
/*jshint browser: true */
|
||||
/*global define */
|
||||
'use strict';
|
||||
|
||||
define(function() {
|
||||
var api;
|
||||
|
||||
api = {
|
||||
setInteractive: function() {},
|
||||
useLocalizedStrings: function() {},
|
||||
|
||||
viewAccounts: function() {
|
||||
var account = {
|
||||
id: 'fake_account',
|
||||
name: 'fake account'
|
||||
};
|
||||
|
||||
var acctsSlice = {
|
||||
items: [
|
||||
account
|
||||
],
|
||||
defaultAccount: account,
|
||||
die: function() {}
|
||||
};
|
||||
|
||||
setTimeout(function() {
|
||||
if (!acctsSlice.oncomplete) {
|
||||
return;
|
||||
}
|
||||
|
||||
acctsSlice.oncomplete();
|
||||
});
|
||||
|
||||
return acctsSlice;
|
||||
},
|
||||
|
||||
viewFolderMessages: function() {
|
||||
var messagesSlice = {
|
||||
items: [],
|
||||
die: function() {}
|
||||
};
|
||||
|
||||
return messagesSlice;
|
||||
},
|
||||
|
||||
viewFolders: function(mode, argument) {
|
||||
var inboxFolder = {
|
||||
id: 'fake_inbox',
|
||||
type: 'inbox',
|
||||
name: 'inbox'
|
||||
};
|
||||
|
||||
var foldersSlice = {
|
||||
items: [
|
||||
inboxFolder
|
||||
],
|
||||
|
||||
getFirstFolderWithType: function(type) {
|
||||
if (type !== 'inbox') {
|
||||
throw new Error('Only type of inbox supported in mock_api');
|
||||
}
|
||||
|
||||
return inboxFolder;
|
||||
},
|
||||
|
||||
die: function() {}
|
||||
};
|
||||
|
||||
setTimeout(function() {
|
||||
if (!foldersSlice.oncomplete) {
|
||||
return;
|
||||
}
|
||||
|
||||
foldersSlice.oncomplete();
|
||||
});
|
||||
|
||||
return foldersSlice;
|
||||
}
|
||||
};
|
||||
|
||||
return api;
|
||||
});
|
||||
49
gaia/apps/email/test/unit/model_acctsslice_startup_test.js
Executable file
49
gaia/apps/email/test/unit/model_acctsslice_startup_test.js
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
'use strict';
|
||||
/*jshint browser: true */
|
||||
/*global requireApp, suite, testConfig, test, assert,
|
||||
suiteSetup, suiteTeardown */
|
||||
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
|
||||
suite('model acctsSlice, startup', function() {
|
||||
var api, model;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig(
|
||||
{
|
||||
suiteTeardown: suiteTeardown,
|
||||
done: done
|
||||
},
|
||||
['api', 'model_create'],
|
||||
function(a, mc) {
|
||||
api = a;
|
||||
model = mc.defaultModel;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('consistent view of acctsSlice initialization', function(done) {
|
||||
var fakeSlice = {
|
||||
oncomplete: function() { },
|
||||
items: [],
|
||||
die: function() { }
|
||||
};
|
||||
sinon.stub(api, 'viewAccounts').returns(fakeSlice);
|
||||
// Wait for model.init to finish this tick...
|
||||
setTimeout(function() {
|
||||
assert.ok(!model.acctsSlice,
|
||||
'model.acctsSlice should not be set ' +
|
||||
'before acctsSlice.oncomplete fires');
|
||||
|
||||
fakeSlice.oncomplete();
|
||||
|
||||
assert.ok(model.acctsSlice,
|
||||
'model.acctsSlice _should_ be set ' +
|
||||
'after acctsSlice.oncomplete fires');
|
||||
done();
|
||||
});
|
||||
model.init();
|
||||
});
|
||||
});
|
||||
|
||||
53
gaia/apps/email/test/unit/model_create_test.js
Executable file
53
gaia/apps/email/test/unit/model_create_test.js
Executable file
|
|
@ -0,0 +1,53 @@
|
|||
'use strict';
|
||||
/*jshint browser: true */
|
||||
/*global requireApp, suite, setup, testConfig, test, assert,
|
||||
suiteSetup, suiteTeardown */
|
||||
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
|
||||
suite('model_create', function() {
|
||||
var model;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig(
|
||||
{
|
||||
suiteTeardown: suiteTeardown,
|
||||
done: done
|
||||
},
|
||||
['model_create'],
|
||||
function(mc) {
|
||||
model = mc.defaultModel;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
suite('no account by default', function() {
|
||||
test('hasAccount false', function() {
|
||||
assert.equal(model.hasAccount(), false);
|
||||
assert.equal(model.inited, false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
suite('load default account', function() {
|
||||
var account, acctsSlice, foldersSlice;
|
||||
|
||||
setup(function(done) {
|
||||
model.init();
|
||||
model.latestOnce('foldersSlice', function() {
|
||||
account = model.account;
|
||||
acctsSlice = model.acctsSlice;
|
||||
foldersSlice = model.foldersSlice;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('hasAccount true', function() {
|
||||
assert.equal(model.hasAccount(), true);
|
||||
assert.equal(model.inited, true);
|
||||
assert.equal(acctsSlice.defaultAccount === account, true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
75
gaia/apps/email/test/unit/query_string_test.js
Executable file
75
gaia/apps/email/test/unit/query_string_test.js
Executable file
|
|
@ -0,0 +1,75 @@
|
|||
'use strict';
|
||||
/*jshint browser: true */
|
||||
/*global requireApp, suite, testConfig, test, assert,
|
||||
suiteSetup, suiteTeardown */
|
||||
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
|
||||
suite('query_string', function() {
|
||||
var queryString,
|
||||
string1 = 'type=message_list&accountId=A',
|
||||
object1 = {
|
||||
type: 'message_list',
|
||||
accountId: 'A'
|
||||
},
|
||||
string2 = 'type=message_reader&accountId=B&messageSuid=B%2F1',
|
||||
object2 = {
|
||||
type: 'message_reader',
|
||||
accountId: 'B',
|
||||
messageSuid: 'B/1'
|
||||
};
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig(
|
||||
{
|
||||
suiteTeardown: suiteTeardown,
|
||||
done: done
|
||||
},
|
||||
['query_string'],
|
||||
function(q) {
|
||||
queryString = q;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
suite('#toObject', function() {
|
||||
|
||||
test('basic use', function() {
|
||||
var result1 = queryString.toObject(string1),
|
||||
result2 = queryString.toObject(string2);
|
||||
|
||||
assert.equal('message_list', result1.type);
|
||||
assert.equal('A', result1.accountId);
|
||||
|
||||
assert.equal('message_reader', result2.type);
|
||||
assert.equal('B', result2.accountId);
|
||||
assert.equal('B/1', result2.messageSuid);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('#fromObject', function() {
|
||||
|
||||
test('basic use', function() {
|
||||
var result1 = queryString.fromObject(object1),
|
||||
result2 = queryString.fromObject(object2);
|
||||
|
||||
assert.equal(string1, result1);
|
||||
assert.equal(string2, result2);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('encodeURIComponent use', function() {
|
||||
|
||||
test('basic use', function() {
|
||||
var result1 = queryString
|
||||
.toObject('url%26type=http%3A%2F%2Fmozilla.com%2F');
|
||||
|
||||
assert.equal('http://mozilla.com/', result1['url&type']);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
102
gaia/apps/email/test/unit/setup_manual_config_protocol_dropdown_disabled.js
Executable file
102
gaia/apps/email/test/unit/setup_manual_config_protocol_dropdown_disabled.js
Executable file
|
|
@ -0,0 +1,102 @@
|
|||
/*global requireApp, suite, setup, testConfig, test, assert,
|
||||
suiteSetup, suiteTeardown, Event */
|
||||
'use strict';
|
||||
|
||||
requireApp('email/js/alameda.js');
|
||||
requireApp('email/test/config.js');
|
||||
suite('IMAP protocol dropdown', function() {
|
||||
var smc;
|
||||
var SetupManualConfig;
|
||||
var el;
|
||||
|
||||
suiteSetup(function(done) {
|
||||
testConfig({
|
||||
suiteTeardown: suiteTeardown,
|
||||
done: done,
|
||||
defines: {}
|
||||
},
|
||||
['element!cards/setup_manual_config'], function(smc) {
|
||||
SetupManualConfig = smc;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
smc = new SetupManualConfig();
|
||||
/*
|
||||
el = document.createElement('div');
|
||||
el.style.width = '100%';
|
||||
el.style.height = '100%';
|
||||
el.appendChild(smc);
|
||||
var refNode = document.childNodes[0];
|
||||
if (refNode) {
|
||||
document.insertBefore(el, refNode);
|
||||
} else {
|
||||
document.appendChild(el);
|
||||
}
|
||||
*/
|
||||
});
|
||||
|
||||
suite('IMAP port selection', function() {
|
||||
// TEST DISABLED: see comments below, some weirdness with custom elements
|
||||
// receiving the change event in this test.
|
||||
test('should change upon selecting STARTTLS', function(done) {
|
||||
var dropdown = smc.getElementsByClassName(
|
||||
'sup-manual-composite-socket')[0];
|
||||
var port = smc.getElementsByClassName('sup-manual-composite-port')[0];
|
||||
assert.equal(port.value, '993');
|
||||
assert.equal(dropdown.value, 'SSL');
|
||||
/*
|
||||
var oldFn = smc.onChangeCompositeSocket;
|
||||
smc.onChangeCompositeSocket = function(event) {
|
||||
oldFn.call(smc, event);
|
||||
|
||||
assert.equal(port.value, '143');
|
||||
done();
|
||||
};
|
||||
*/
|
||||
dropdown.value = 'STARTTLS';
|
||||
// TODO: this event dispatch does not seem to actually trigger a change
|
||||
// event on the dropdown element. See commented out code above. Theory:
|
||||
// with a custom element, not really part of the DOM until inserted in the
|
||||
// document, and then events would fire, but document.appendChild() fails
|
||||
// in this test harness.
|
||||
dropdown.dispatchEvent(new Event('change'));
|
||||
assert.equal(port.value, '143');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
suite('SMTP port selection', function() {
|
||||
// TEST DISABLED: see comments below, some weirdness with custom elements
|
||||
// receiving the change event in this test.
|
||||
test('should change upon selecting STARTTLS', function(done) {
|
||||
var dropdown = smc.getElementsByClassName('sup-manual-smtp-socket')[0];
|
||||
var port = smc.getElementsByClassName('sup-manual-smtp-port')[0];
|
||||
assert.equal(port.value, '465');
|
||||
assert.equal(dropdown.value, 'SSL');
|
||||
/*
|
||||
var oldFn = smc.onChangeSmtpSocket;
|
||||
smc.onChangeSmtpSocket = function(event) {
|
||||
oldFn.call(smc, event);
|
||||
assert.equal(port.value, '587');
|
||||
done();
|
||||
};
|
||||
*/
|
||||
dropdown.value = 'STARTTLS';
|
||||
// TODO: this event dispatch does not seem to actually trigger a change
|
||||
// event on the dropdown element. See commented out code above. Theory:
|
||||
// with a custom element, not really part of the DOM until inserted in the
|
||||
// document, and then events would fire, but document.appendChild() fails
|
||||
// in this test harness.
|
||||
dropdown.dispatchEvent(new Event('change'));
|
||||
assert.equal(port.value, '587');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
smc = null;
|
||||
el = null;
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue