1
0
mirror of https://github.com/moparisthebest/mail synced 2024-11-26 02:42:17 -05:00

Merge pull request #221 from whiteout-io/dev/WO-707

Dev/wo 707
This commit is contained in:
Tankred Hase 2014-12-05 13:04:32 +01:00
commit 2272f3d58f
35 changed files with 325 additions and 360 deletions

View File

@ -159,7 +159,6 @@ module.exports = function(grunt) {
'test/unit/util/dialog-test.js', 'test/unit/util/dialog-test.js',
'test/unit/util/connection-doctor-test.js', 'test/unit/util/connection-doctor-test.js',
'test/unit/util/update-handler-test.js', 'test/unit/util/update-handler-test.js',
'test/unit/util/backbutton-handler-test.js',
'test/unit/util/status-display-test.js', 'test/unit/util/status-display-test.js',
'test/unit/crypto/pgp-test.js', 'test/unit/crypto/pgp-test.js',
'test/unit/crypto/crypto-test.js', 'test/unit/crypto/crypto-test.js',

View File

@ -48,7 +48,7 @@ For development you can start a connect dev server:
grunt dev grunt dev
Then visit [http://localhost:8580/dist/#/desktop?dev=true](http://localhost:8580/dist/#/desktop?dev=true) for front-end code or [http://localhost:8580/test/unit/](http://localhost:8580/test/unit/) to test JavaScript changes. You can also start a watch task so you don't have rebuild everytime you make a change: Then visit [http://localhost:8580/dist/#/account?dev=true](http://localhost:8580/dist/#/account?dev=true) for front-end code or [http://localhost:8580/test/unit/](http://localhost:8580/test/unit/) to test JavaScript changes. You can also start a watch task so you don't have rebuild everytime you make a change:
grunt watch grunt watch

View File

@ -35,8 +35,7 @@ var axe = require('axe-logger'),
WriteCtrl = require('./controller/app/write'), WriteCtrl = require('./controller/app/write'),
NavigationCtrl = require('./controller/app/navigation'), NavigationCtrl = require('./controller/app/navigation'),
ActionBarCtrl = require('./controller/app/action-bar'), ActionBarCtrl = require('./controller/app/action-bar'),
StatusDisplayCtrl = require('./controller/app/status-display'), StatusDisplayCtrl = require('./controller/app/status-display');
backButtonUtil = require('./util/backbutton-handler');
// include angular modules // include angular modules
require('./app-config'); require('./app-config');
@ -104,9 +103,10 @@ app.config(function($routeProvider, $animateProvider) {
templateUrl: 'tpl/login-privatekey-download.html', templateUrl: 'tpl/login-privatekey-download.html',
controller: LoginPrivateKeyDownloadCtrl controller: LoginPrivateKeyDownloadCtrl
}); });
$routeProvider.when('/desktop', { $routeProvider.when('/account', {
templateUrl: 'tpl/desktop.html', templateUrl: 'tpl/desktop.html',
controller: NavigationCtrl controller: NavigationCtrl,
reloadOnSearch: false // don't reload controllers in main app when query params change
}); });
$routeProvider.otherwise({ $routeProvider.otherwise({
redirectTo: '/login' redirectTo: '/login'
@ -119,10 +119,6 @@ app.config(function($routeProvider, $animateProvider) {
app.run(function($rootScope) { app.run(function($rootScope) {
// global state... inherited to all child scopes // global state... inherited to all child scopes
$rootScope.state = {}; $rootScope.state = {};
// attach the back button handler to the root scope
backButtonUtil.attachHandler($rootScope);
// attach fastclick // attach fastclick
FastClick.attach(document.body); FastClick.attach(document.body);
}); });

View File

@ -2,6 +2,10 @@
var AboutCtrl = function($scope, appConfig) { var AboutCtrl = function($scope, appConfig) {
//
// scope state
//
$scope.state.about = { $scope.state.about = {
toggle: function(to) { toggle: function(to) {
$scope.state.lightbox = (to) ? 'about' : undefined; $scope.state.lightbox = (to) ? 'about' : undefined;
@ -15,9 +19,6 @@ var AboutCtrl = function($scope, appConfig) {
$scope.version = appConfig.config.appVersion + ' (beta)'; $scope.version = appConfig.config.appVersion + ' (beta)';
$scope.date = new Date(); $scope.date = new Date();
//
// scope functions
//
}; };
module.exports = AboutCtrl; module.exports = AboutCtrl;

View File

@ -6,6 +6,10 @@ var AccountCtrl = function($scope, auth, keychain, pgp, appConfig, download, dia
return; return;
} }
//
// scope state
//
$scope.state.account = { $scope.state.account = {
toggle: function(to) { toggle: function(to) {
$scope.state.lightbox = (to) ? 'account' : undefined; $scope.state.lightbox = (to) ? 'account' : undefined;
@ -46,6 +50,7 @@ var AccountCtrl = function($scope, auth, keychain, pgp, appConfig, download, dia
}); });
}); });
}; };
}; };
module.exports = AccountCtrl; module.exports = AccountCtrl;

View File

@ -2,7 +2,11 @@
var JUNK_FOLDER_TYPE = 'Junk'; var JUNK_FOLDER_TYPE = 'Junk';
var ActionBarCtrl = function($scope, email, dialog, statusDisplay) { var ActionBarCtrl = function($scope, email, dialog, status) {
//
// scope functions
//
/** /**
* Move a single message from the currently selected folder to another folder * Move a single message from the currently selected folder to another folder
@ -15,9 +19,9 @@ var ActionBarCtrl = function($scope, email, dialog, statusDisplay) {
} }
// close read state // close read state
$scope.state.read.open = false; status.setReading(false);
// show message
statusDisplay.update('Moving message...'); status.update('Moving message...');
email.moveMessage({ email.moveMessage({
folder: currentFolder(), folder: currentFolder(),
@ -28,14 +32,14 @@ var ActionBarCtrl = function($scope, email, dialog, statusDisplay) {
// show errors where appropriate // show errors where appropriate
if (err.code === 42) { if (err.code === 42) {
$scope.select(message); $scope.select(message);
statusDisplay.update('Unable to move message in offline mode!'); status.update('Unable to move message in offline mode!');
return; return;
} }
statusDisplay.update('Error during move!'); status.update('Error during move!');
dialog.error(err); dialog.error(err);
return; return;
} }
statusDisplay.update('Message moved.'); status.update('Message moved.');
$scope.$apply(); $scope.$apply();
}); });
}; };
@ -76,9 +80,8 @@ var ActionBarCtrl = function($scope, email, dialog, statusDisplay) {
} }
// close read state // close read state
$scope.state.read.open = false; status.setReading(false);
status.update('Deleting message...');
statusDisplay.update('Deleting message...');
email.deleteMessage({ email.deleteMessage({
folder: currentFolder(), folder: currentFolder(),
@ -88,14 +91,14 @@ var ActionBarCtrl = function($scope, email, dialog, statusDisplay) {
// show errors where appropriate // show errors where appropriate
if (err.code === 42) { if (err.code === 42) {
$scope.select(message); $scope.select(message);
statusDisplay.update('Unable to delete message in offline mode!'); status.update('Unable to delete message in offline mode!');
return; return;
} }
statusDisplay.update('Error during delete!'); status.update('Error during delete!');
dialog.error(err); dialog.error(err);
return; return;
} }
statusDisplay.update('Message deleted.'); status.update('Message deleted.');
$scope.$apply(); $scope.$apply();
}); });
}; };
@ -118,11 +121,11 @@ var ActionBarCtrl = function($scope, email, dialog, statusDisplay) {
return; return;
} }
statusDisplay.update('Updating unread flag...'); status.update('Updating unread flag...');
// close read state // close read state
if (!keepOpen) { if (!keepOpen) {
$scope.state.read.open = false; status.setReading(false);
} }
var originalState = message.unread; var originalState = message.unread;
@ -134,17 +137,17 @@ var ActionBarCtrl = function($scope, email, dialog, statusDisplay) {
if (err && err.code === 42) { if (err && err.code === 42) {
// offline, restore // offline, restore
message.unread = originalState; message.unread = originalState;
statusDisplay.update('Unable to mark message in offline mode!'); status.update('Unable to mark message in offline mode!');
return; return;
} }
if (err) { if (err) {
statusDisplay.update('Error on sync!'); status.update('Error on sync!');
dialog.error(err); dialog.error(err);
return; return;
} }
statusDisplay.update('Online'); status.update('Online');
$scope.$apply(); $scope.$apply();
}); });
}; };
@ -169,7 +172,7 @@ var ActionBarCtrl = function($scope, email, dialog, statusDisplay) {
return; return;
} }
statusDisplay.update(flagged ? 'Adding star to message...' : 'Removing star from message'); status.update(flagged ? 'Adding star to message...' : 'Removing star from message');
var originalState = message.flagged; var originalState = message.flagged;
message.flagged = flagged; message.flagged = flagged;
@ -180,17 +183,17 @@ var ActionBarCtrl = function($scope, email, dialog, statusDisplay) {
if (err && err.code === 42) { if (err && err.code === 42) {
// offline, restore // offline, restore
message.unread = originalState; message.unread = originalState;
statusDisplay.update('Unable to ' + (flagged ? 'add star to' : 'remove star from') + ' message in offline mode!'); status.update('Unable to ' + (flagged ? 'add star to' : 'remove star from') + ' message in offline mode!');
return; return;
} }
if (err) { if (err) {
statusDisplay.update('Error on sync!'); status.update('Error on sync!');
dialog.error(err); dialog.error(err);
return; return;
} }
statusDisplay.update('Online'); status.update('Online');
$scope.$apply(); $scope.$apply();
}); });
}; };

View File

@ -6,10 +6,13 @@
var ContactsCtrl = function($scope, keychain, pgp, dialog) { var ContactsCtrl = function($scope, keychain, pgp, dialog) {
//
// scope state
//
$scope.state.contacts = { $scope.state.contacts = {
toggle: function(to) { toggle: function(to) {
$scope.state.lightbox = (to) ? 'contacts' : undefined; $scope.state.lightbox = (to) ? 'contacts' : undefined;
$scope.listKeys(); $scope.listKeys();
} }
}; };
@ -93,6 +96,7 @@ var ContactsCtrl = function($scope, keychain, pgp, dialog) {
$scope.listKeys(); $scope.listKeys();
}); });
}; };
}; };
// //

View File

@ -2,6 +2,10 @@
var DialogCtrl = function($scope, dialog) { var DialogCtrl = function($scope, dialog) {
//
// scope state
//
$scope.state.dialog = { $scope.state.dialog = {
open: false open: false
}; };

View File

@ -1,20 +1,20 @@
'use strict'; 'use strict';
var searchTimeout, firstSelect; var searchTimeout;
// //
// Constants // Constants
// //
var INIT_DISPLAY_LEN = 20, var INIT_DISPLAY_LEN = 50,
SCROLL_DISPLAY_LEN = 10, SCROLL_DISPLAY_LEN = 10,
FOLDER_TYPE_INBOX = 'Inbox', FOLDER_TYPE_INBOX = 'Inbox',
NOTIFICATION_INBOX_TIMEOUT = 5000; NOTIFICATION_INBOX_TIMEOUT = 5000;
var MailListCtrl = function($scope, $timeout, $routeParams, $filter, statusDisplay, notification, email, keychain, dialog, search, dummy) { var MailListCtrl = function($scope, $timeout, $location, $filter, status, notification, email, keychain, dialog, search, dummy) {
// //
// Init // scope state
// //
$scope.state.mailList = {}; $scope.state.mailList = {};
@ -24,6 +24,30 @@ var MailListCtrl = function($scope, $timeout, $routeParams, $filter, statusDispl
*/ */
$scope.pendingNotifications = []; $scope.pendingNotifications = [];
//
// url/history handling
//
/**
* Set the route to a message which will go to read mode
*/
$scope.navigate = function(message) {
$location.search('uid', message.uid);
};
$scope.loc = $location;
$scope.$watch('(loc.search()).uid', function(uid) {
if (typeof uid === 'undefined') {
// no uid specified in url... select no message
$scope.select();
return;
}
// select the message specified by the uid in the url
$scope.select(_.findWhere(currentFolder().messages, {
uid: typeof uid === 'string' ? parseInt(uid, 10) : uid
}));
});
// //
// scope functions // scope functions
// //
@ -62,11 +86,10 @@ var MailListCtrl = function($scope, $timeout, $routeParams, $filter, statusDispl
$scope.state.mailList.selected = message; $scope.state.mailList.selected = message;
if (!firstSelect) { if ($location.search().dev) {
// only toggle to read view on 2nd select in mobile mode // stop here in dev mode
$scope.state.read.toggle(true); return;
} }
firstSelect = false;
keychain.refreshKeyForUserId({ keychain.refreshKeyForUserId({
userId: message.from[0].address userId: message.from[0].address
@ -132,8 +155,8 @@ var MailListCtrl = function($scope, $timeout, $routeParams, $filter, statusDispl
$scope.searchText = undefined; $scope.searchText = undefined;
// in development, display dummy mail objects // in development, display dummy mail objects
if ($routeParams.dev) { if ($location.search().dev) {
statusDisplay.update('Last update: ', new Date()); status.update('Last update: ', new Date());
currentFolder().messages = dummy.listMails(); currentFolder().messages = dummy.listMails();
return; return;
} }
@ -148,15 +171,13 @@ var MailListCtrl = function($scope, $timeout, $routeParams, $filter, statusDispl
} }
// sort message by uid // sort message by uid
currentFolder().messages.sort(byUidDescending); messages.sort(byUidDescending);
// set display buffer to first messages // Unselect message if it has been deleted from the messages array
$scope.displayMessages = currentFolder().messages.slice(0, INIT_DISPLAY_LEN); if (messages.indexOf(currentMessage()) === -1) {
$scope.select();
// Shows the next message based on the uid of the currently selected element
if (currentFolder().messages.indexOf(currentMessage()) === -1) {
firstSelect = true; // reset first selection
$scope.select($scope.displayMessages[0]);
} }
// set display buffer to first messages
$scope.displayMessages = messages.slice(0, INIT_DISPLAY_LEN);
}); });
/** /**
@ -200,20 +221,20 @@ var MailListCtrl = function($scope, $timeout, $routeParams, $filter, statusDispl
if (!searchText) { if (!searchText) {
// set display buffer to first messages // set display buffer to first messages
$scope.displayMessages = currentFolder().messages.slice(0, INIT_DISPLAY_LEN); $scope.displayMessages = currentFolder().messages.slice(0, INIT_DISPLAY_LEN);
statusDisplay.setSearching(false); status.setSearching(false);
statusDisplay.update('Online'); status.update('Online');
return; return;
} }
// display searching spinner // display searching spinner
statusDisplay.setSearching(true); status.setSearching(true);
statusDisplay.update('Searching ...'); status.update('Searching ...');
searchTimeout = setTimeout(function() { searchTimeout = setTimeout(function() {
$scope.$apply(function() { $scope.$apply(function() {
// filter relevant messages // filter relevant messages
$scope.displayMessages = search.filter(currentFolder().messages, searchText); $scope.displayMessages = search.filter(currentFolder().messages, searchText);
statusDisplay.setSearching(false); status.setSearching(false);
statusDisplay.update('Matches in this folder'); status.update('Matches in this folder');
}); });
}, 500); }, 500);
}; };
@ -225,10 +246,10 @@ var MailListCtrl = function($scope, $timeout, $routeParams, $filter, statusDispl
// wait one cycle for the status display controllers to init // wait one cycle for the status display controllers to init
$timeout(function() { $timeout(function() {
if (isOnline) { if (isOnline) {
statusDisplay.update('Online'); status.update('Online');
openCurrentFolder(); openCurrentFolder();
} else { } else {
statusDisplay.update('Offline mode'); status.update('Offline mode');
} }
}); });
}, true); }, true);
@ -268,7 +289,7 @@ var MailListCtrl = function($scope, $timeout, $routeParams, $filter, statusDispl
// Notification API // Notification API
// //
(email || {}).onIncomingMessage = function(msgs) { email.onIncomingMessage = function(msgs) {
var note, title, message, unreadMsgs; var note, title, message, unreadMsgs;
unreadMsgs = msgs.filter(function(msg) { unreadMsgs = msgs.filter(function(msg) {
@ -291,17 +312,13 @@ var MailListCtrl = function($scope, $timeout, $routeParams, $filter, statusDispl
title: title, title: title,
message: message, message: message,
onClick: function() { onClick: function() {
// force toggle into read mode when notification is clicked
firstSelect = false;
// remove from pending notificatiosn // remove from pending notificatiosn
var index = $scope.pendingNotifications.indexOf(note); var index = $scope.pendingNotifications.indexOf(note);
if (index !== -1) { if (index !== -1) {
$scope.pendingNotifications.splice(index, 1); $scope.pendingNotifications.splice(index, 1);
} }
// open the message
// mark message as read $scope.navigate(_.findWhere(currentFolder().messages, {
$scope.select(_.findWhere(currentFolder().messages, {
uid: unreadMsgs[0].uid uid: unreadMsgs[0].uid
})); }));
}, },

View File

@ -1,7 +1,5 @@
'use strict'; 'use strict';
var backBtnHandler = require('../../util/backbutton-handler');
// //
// Constants // Constants
// //
@ -13,8 +11,8 @@ var NOTIFICATION_SENT_TIMEOUT = 2000;
// Controller // Controller
// //
var NavigationCtrl = function($scope, $routeParams, $location, account, email, outbox, notification, appConfig, dialog) { var NavigationCtrl = function($scope, $location, account, email, outbox, notification, appConfig, dialog, dummy) {
if (!$routeParams.dev && !account.isLoggedIn()) { if (!$location.search().dev && !account.isLoggedIn()) {
$location.path('/'); // init app $location.path('/'); // init app
return; return;
} }
@ -23,7 +21,7 @@ var NavigationCtrl = function($scope, $routeParams, $location, account, email, o
config = appConfig.config; config = appConfig.config;
// //
// scope functions // scope state
// //
$scope.state.nav = { $scope.state.nav = {
@ -33,6 +31,36 @@ var NavigationCtrl = function($scope, $routeParams, $location, account, email, o
} }
}; };
//
// url/history handling
//
$scope.loc = $location;
// nav open/close state url watcher
$scope.$watch('(loc.search()).nav', function(open) {
// synchronize the url to the scope state
$scope.state.nav.toggle(!!open);
});
$scope.$watch('state.nav.open', function(value) {
// synchronize the scope state to the url
$location.search('nav', value ? true : null);
});
// lightbox state url watcher
$scope.$watch('(loc.search()).lightbox', function(value) {
// synchronize the url to the scope state
$scope.state.lightbox = (value) ? value : undefined;
});
$scope.$watch('state.lightbox', function(value) {
// synchronize the scope state to the url
$location.search('lightbox', value ? value : null);
});
//
// scope functions
//
$scope.openFolder = function(folder) { $scope.openFolder = function(folder) {
$scope.state.nav.currentFolder = folder; $scope.state.nav.currentFolder = folder;
$scope.state.nav.toggle(false); $scope.state.nav.toggle(false);
@ -72,15 +100,23 @@ var NavigationCtrl = function($scope, $routeParams, $location, account, email, o
// Start // Start
// //
// handle back button
backBtnHandler.start();
// init folders // init folders
initializeFolders(); initializeFolders();
// select inbox as the current folder on init // folder index url watcher
if ($scope.account.folders && $scope.account.folders.length > 0) { $scope.$watch('(loc.search()).folder', function(folderIndex) {
$scope.openFolder($scope.account.folders[0]); if (typeof folderIndex === 'undefined') {
$location.search('folder', 0); // navigate to inbox by default
return;
} }
// select current folder
folderIndex = typeof folderIndex === 'string' ? parseInt(folderIndex, 10) : folderIndex;
if ($scope.account.folders && $scope.account.folders.length > folderIndex) {
// navigate to the selected folder index
$scope.openFolder($scope.account.folders[folderIndex]);
}
});
// connect imap/smtp clients on first startup // connect imap/smtp clients on first startup
account.onConnect(function(err) { account.onConnect(function(err) {
if (err) { if (err) {
@ -101,8 +137,9 @@ var NavigationCtrl = function($scope, $routeParams, $location, account, email, o
function initializeFolders() { function initializeFolders() {
// create dummy folder in dev environment only // create dummy folder in dev environment only
if ($routeParams.dev) { if ($location.search().dev) {
createDummyFolders(); $scope.$root.account = {};
$scope.account.folders = dummy.listFolders();
return; return;
} }
@ -122,33 +159,6 @@ var NavigationCtrl = function($scope, $routeParams, $location, account, email, o
timeout: NOTIFICATION_SENT_TIMEOUT timeout: NOTIFICATION_SENT_TIMEOUT
}, function() {}); }, function() {});
} }
// attach dummy folders for development
function createDummyFolders() {
$scope.$root.account = {};
$scope.account.folders = [{
type: 'Inbox',
count: 2,
path: 'INBOX'
}, {
type: 'Sent',
count: 0,
path: 'SENT'
}, {
type: config.outboxMailboxType,
count: 0,
path: config.outboxMailboxPath
}, {
type: 'Drafts',
count: 0,
path: 'DRAFTS'
}, {
type: 'Trash',
count: 0,
path: 'TRASH'
}];
}
}; };
// //

View File

@ -4,6 +4,10 @@ var util = require('crypto-lib').util;
var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) { var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) {
//
// scope state
//
$scope.state.privateKeyUpload = { $scope.state.privateKeyUpload = {
toggle: function(to) { toggle: function(to) {
// open lightbox // open lightbox
@ -33,6 +37,10 @@ var PrivateKeyUploadCtrl = function($scope, keychain, pgp, dialog, auth) {
} }
}; };
//
// scope functions
//
$scope.checkServerForKey = function(callback) { $scope.checkServerForKey = function(callback) {
var keyParams = pgp.getKeyParams(); var keyParams = pgp.getKeyParams();
keychain.hasPrivateKey({ keychain.hasPrivateKey({

View File

@ -4,12 +4,13 @@
// Controller // Controller
// //
var ReadCtrl = function($scope, email, invitation, outbox, pgp, keychain, appConfig, download, auth, dialog) { var ReadCtrl = function($scope, $location, email, invitation, outbox, pgp, keychain, appConfig, download, auth, dialog) {
var str = appConfig.string; var str = appConfig.string;
// set default value so that the popover height is correct on init //
$scope.keyId = 'No key found.'; // scope state
//
$scope.state.read = { $scope.state.read = {
open: false, open: false,
@ -18,7 +19,39 @@ var ReadCtrl = function($scope, email, invitation, outbox, pgp, keychain, appCon
} }
}; };
$scope.$on('read', function(e, state) {
$scope.state.read.toggle(state);
});
// set default value so that the popover height is correct on init
$scope.keyId = 'No key found.';
//
// url/history handling
//
// read state url watcher
$scope.loc = $location;
$scope.$watch('(loc.search()).uid', function(uid) {
// synchronize the url to the scope state
$scope.state.read.toggle(!!uid);
});
$scope.$watch('state.read.open', function(value) {
// close read mode by navigating to folder view
if (!value) {
$location.search('uid', null);
}
});
//
// scope functions
//
$scope.getKeyId = function(address) { $scope.getKeyId = function(address) {
if ($location.search().dev || !address) {
return;
}
$scope.keyId = 'Searching...'; $scope.keyId = 'Searching...';
keychain.getReceiverPublicKey(address, function(err, pubkey) { keychain.getReceiverPublicKey(address, function(err, pubkey) {
if (err) { if (err) {
@ -41,7 +74,7 @@ var ReadCtrl = function($scope, email, invitation, outbox, pgp, keychain, appCon
}; };
$scope.$watch('state.mailList.selected', function(mail) { $scope.$watch('state.mailList.selected', function(mail) {
if (!mail) { if ($location.search().dev || !mail) {
return; return;
} }

View File

@ -43,7 +43,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, k
return; return;
} }
$location.path('/desktop'); $location.path('/account');
$scope.$apply(); $scope.$apply();
}); });
} }

View File

@ -65,7 +65,7 @@ var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter, ema
return; return;
} }
$location.path('/desktop'); $location.path('/account');
$scope.$apply(); $scope.$apply();
}); });
}); });

View File

@ -95,7 +95,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams, email, auth, p
return; return;
} }
$location.path('/desktop'); $location.path('/account');
$scope.$apply(); $scope.$apply();
}); });
} }

View File

@ -100,7 +100,7 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams, auth
return; return;
} }
$scope.goTo('/desktop'); $scope.goTo('/account');
}); });
}); });
}); });

View File

@ -60,7 +60,7 @@ var LoginCtrl = function($scope, $timeout, $location, updateHandler, account, au
return dialog.error(err); return dialog.error(err);
} }
$scope.goTo('/desktop'); $scope.goTo('/account');
}); });
}); });
} else if (availableKeys && availableKeys.publicKey && !availableKeys.privateKey) { } else if (availableKeys && availableKeys.publicKey && !availableKeys.privateKey) {

View File

@ -1,55 +0,0 @@
'use strict';
var axe = require('axe-logger'),
DEBUG_TAG = 'backbutton handler';
/**
* The back button handler introduces meaningful behavior fo rthe back button:
* if there's an open lightbox, close it;
* if the reader is open in mobile mode, close it;
* if the navigation is open, close it;
* if there's nothing else open, shut down the app;
*
* @type {Object}
*/
var backBtnHandler = {
attachHandler: function(scope) {
this.scope = scope;
},
start: function() {
document.addEventListener("backbutton", handleBackButton, false);
},
stop: function() {
document.removeEventListener("backbutton", handleBackButton, false);
}
};
function handleBackButton(event) {
axe.debug(DEBUG_TAG, 'back button pressed');
// this disarms the default behavior which we NEVER want
event.preventDefault();
event.stopPropagation();
if (backBtnHandler.scope.state.lightbox) {
// closes the lightbox (error msgs, writer, ...)
backBtnHandler.scope.state.lightbox = undefined;
axe.debug(DEBUG_TAG, 'lightbox closed');
backBtnHandler.scope.$apply();
} else if (backBtnHandler.scope.state.read && backBtnHandler.scope.state.read.open) {
// closes the reader
backBtnHandler.scope.state.read.toggle(false);
axe.debug(DEBUG_TAG, 'reader closed');
backBtnHandler.scope.$apply();
} else if (backBtnHandler.scope.state.nav && backBtnHandler.scope.state.nav.open) {
// closes the navigation
backBtnHandler.scope.state.nav.toggle(false);
axe.debug(DEBUG_TAG, 'navigation closed');
backBtnHandler.scope.$apply();
} else {
// exits the app
navigator.app.exitApp();
}
}
module.exports = backBtnHandler;

View File

@ -6,6 +6,50 @@ module.exports = Dummy;
function Dummy() {} function Dummy() {}
Dummy.prototype.listFolders = function() {
var dummies = [{
type: 'Inbox',
count: 2,
path: 'INBOX',
wellknown: true
}, {
type: 'Sent',
count: 0,
path: 'SENT',
wellknown: true
}, {
type: 'Outbox',
count: 0,
path: 'OUTBOX',
wellknown: true
}, {
type: 'Drafts',
count: 0,
path: 'DRAFTS',
wellknown: true
}, {
type: 'Trash',
count: 0,
path: 'TRASH',
wellknown: true
}, {
type: 'Flagged',
count: 0,
path: 'FLAGGED',
wellknown: true
}, {
name: 'Archive',
count: 0,
path: 'ARCHIVE'
}, {
name: 'Junk',
count: 0,
path: 'JUNK'
}];
return dummies;
};
Dummy.prototype.listMails = function() { Dummy.prototype.listMails = function() {
var uid = 1000000; var uid = 1000000;

View File

@ -7,6 +7,6 @@ require('./dummy');
require('./dialog'); require('./dialog');
require('./connection-doctor'); require('./connection-doctor');
require('./update/update-handler'); require('./update/update-handler');
require('./status-display'); require('./status');
require('./download'); require('./download');
require('./notification'); require('./notification');

View File

@ -1,13 +1,13 @@
'use strict'; 'use strict';
var ngModule = angular.module('woUtil'); var ngModule = angular.module('woUtil');
ngModule.service('statusDisplay', StatusDisplay); ngModule.service('status', Status);
module.exports = StatusDisplay; module.exports = Status;
/** /**
* A central service to display status updates to the user * A central service to display status updates to the user
*/ */
function StatusDisplay($rootScope, axe) { function Status($rootScope, axe) {
this._rootScope = $rootScope; this._rootScope = $rootScope;
this._axe = axe; this._axe = axe;
} }
@ -17,7 +17,7 @@ function StatusDisplay($rootScope, axe) {
* @param {String} text The status message that is to be displayed to the user * @param {String} text The status message that is to be displayed to the user
* @param {Date} time The time of the last update * @param {Date} time The time of the last update
*/ */
StatusDisplay.prototype.update = function(text, time) { Status.prototype.update = function(text, time) {
this._axe.info('status display', text); this._axe.info('status display', text);
this._rootScope.$broadcast('status', text, time); this._rootScope.$broadcast('status', text, time);
}; };
@ -26,6 +26,14 @@ StatusDisplay.prototype.update = function(text, time) {
* Update the searching status to show a spinner while searching * Update the searching status to show a spinner while searching
* @param {Boolean} state If the spinner should be displayed or not * @param {Boolean} state If the spinner should be displayed or not
*/ */
StatusDisplay.prototype.setSearching = function(state) { Status.prototype.setSearching = function(state) {
this._rootScope.$broadcast('searching', state); this._rootScope.$broadcast('searching', state);
}; };
/**
* Update the reading status to communicate between controllers, if we're in read mode.
* @param {Boolean} state If the reade mode should be open or not
*/
Status.prototype.setReading = function(state) {
this._rootScope.$broadcast('read', state);
};

View File

@ -130,11 +130,6 @@
overflow-y: scroll; overflow-y: scroll;
// allow scrolling on iOS // allow scrolling on iOS
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
// hide scrollbar in webkit
&::-webkit-scrollbar {
display: none;
}
} }
&__entries { &__entries {
display: table; // import for mail list entry desktop layout display: table; // import for mail list entry desktop layout
@ -295,7 +290,8 @@
// Modifiers // Modifiers
&:hover { &:hover,
&.wo-touch-active {
background-color: rgba($color-main, 0.15); background-color: rgba($color-main, 0.15);
} }
&--unread { &--unread {

View File

@ -41,7 +41,7 @@
<p class="typo-paragraph"> <p class="typo-paragraph">
<br> <br>
<a href="javascript:;" wo-touch='showDetails = !showDetails; $event.preventDefault()'> <a href="#" wo-touch="$event.preventDefault(); showDetails = !showDetails">
{{showDetails ? "Hide Options" : "Show Options"}} {{showDetails ? "Hide Options" : "Show Options"}}
</a> </a>
<br><br> <br><br>

View File

@ -27,7 +27,7 @@
infinite-scroll-distance="1" infinite-scroll-parent="true"> infinite-scroll-distance="1" infinite-scroll-parent="true">
<li class="mail-list-entry" <li class="mail-list-entry"
ng-class="{'mail-list-entry--active': email === state.mailList.selected, 'mail-list-entry--unread': email.unread, 'mail-list-entry--attachment': email.attachments !== undefined && email.attachments.length > 0}" ng-class="{'mail-list-entry--active': email === state.mailList.selected, 'mail-list-entry--unread': email.unread, 'mail-list-entry--attachment': email.attachments !== undefined && email.attachments.length > 0}"
wo-touch="select(email)" wo-touch="navigate(email)"
ng-repeat="email in displayMessages"> ng-repeat="email in displayMessages">
<ul class="mail-list-entry__flags"> <ul class="mail-list-entry__flags">
<li class="mail-list-entry__flags-unread"></li> <li class="mail-list-entry__flags-unread"></li>

View File

@ -7,7 +7,7 @@
<ul class="nav__folders"> <ul class="nav__folders">
<li ng-repeat="folder in account.folders" ng-if="folder.wellknown" ng-hide="folder.type === 'Outbox' && folder.count < 1" <li ng-repeat="folder in account.folders" ng-if="folder.wellknown" ng-hide="folder.type === 'Outbox' && folder.count < 1"
class="nav__folder" ng-class="{'nav__folder--open': state.nav.currentFolder === folder}"> class="nav__folder" ng-class="{'nav__folder--open': state.nav.currentFolder === folder}">
<a href="#" wo-touch="$event.preventDefault(); openFolder(folder)"> <a href="#/account?folder={{$index}}">
<svg ng-if="folder.type === 'Inbox'" role="presentation"> <svg ng-if="folder.type === 'Inbox'" role="presentation">
<use xlink:href="#icon-inbox" /> <use xlink:href="#icon-inbox" />
</svg> </svg>
@ -38,7 +38,7 @@
<ul class="nav__folders"> <ul class="nav__folders">
<li ng-repeat="folder in account.folders" ng-if="!folder.wellknown" <li ng-repeat="folder in account.folders" ng-if="!folder.wellknown"
class="nav__folder" ng-class="{'nav__folder--open': state.nav.currentFolder === folder}"> class="nav__folder" ng-class="{'nav__folder--open': state.nav.currentFolder === folder}">
<a href="#" wo-touch="$event.preventDefault(); openFolder(folder)"> <a href="#/account?folder={{$index}}">
<svg role="presentation"><use xlink:href="#icon-folder" /></svg> <svg role="presentation"><use xlink:href="#icon-folder" /></svg>
{{folder.name}} {{folder.name}}
<span ng-show="folder.count > 0" class="nav__counter">{{folder.count}}</span> <span ng-show="folder.count > 0" class="nav__counter">{{folder.count}}</span>

View File

@ -6,7 +6,7 @@
<div class="read__folder-toolbar"> <div class="read__folder-toolbar">
<div class="toolbar"> <div class="toolbar">
<a class="toolbar__label" href="javascript:;" wo-touch="state.read.toggle(false)"> <a class="toolbar__label" href="#" wo-touch="$event.preventDefault(); state.read.toggle(false)">
<svg><use xlink:href="#icon-back" /><title>Back</title></svg> <svg><use xlink:href="#icon-back" /><title>Back</title></svg>
{{state.nav.currentFolder.wellknown ? state.nav.currentFolder.type : state.nav.currentFolder.name}} {{state.nav.currentFolder.wellknown ? state.nav.currentFolder.type : state.nav.currentFolder.name}}
</a> </a>

View File

@ -26,26 +26,6 @@ if (!Function.prototype.bind) {
}; };
} }
// a warm round of applause for phantomjs for missing events
(function() {
if (!window.CustomEvent) {
var CustomEvent = function(event, params) {
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
};
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
};
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
}
})();
// //
// Test setup // Test setup
// //

View File

@ -2,26 +2,22 @@
var Email = require('../../../../src/js/email/email'), var Email = require('../../../../src/js/email/email'),
Dialog = require('../../../../src/js/util/dialog'), Dialog = require('../../../../src/js/util/dialog'),
StatusDisplay = require('../../../../src/js/util/status-display'), Status = require('../../../../src/js/util/status'),
ActionBarCtrl = require('../../../../src/js/controller/app/action-bar'); ActionBarCtrl = require('../../../../src/js/controller/app/action-bar');
describe('Action Bar Controller unit test', function() { describe('Action Bar Controller unit test', function() {
var scope, actionBarCtrl, emailMock, dialogMock, statusDisplayMock; var scope, actionBarCtrl, emailMock, dialogMock, statusMock;
beforeEach(function() { beforeEach(function() {
emailMock = sinon.createStubInstance(Email); emailMock = sinon.createStubInstance(Email);
dialogMock = sinon.createStubInstance(Dialog); dialogMock = sinon.createStubInstance(Dialog);
statusDisplayMock = sinon.createStubInstance(StatusDisplay); statusMock = sinon.createStubInstance(Status);
angular.module('actionbartest', []); angular.module('actionbartest', ['woUtil']);
angular.mock.module('actionbartest'); angular.mock.module('actionbartest');
angular.mock.inject(function($rootScope, $controller) { angular.mock.inject(function($rootScope, $controller) {
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = { scope.state = {};
mailList: {
updateStatus: function() {}
}
};
scope.state.nav = { scope.state.nav = {
currentFolder: { currentFolder: {
@ -35,15 +31,11 @@ describe('Action Bar Controller unit test', function() {
} }
}; };
scope.state.read = {
open: true
};
actionBarCtrl = $controller(ActionBarCtrl, { actionBarCtrl = $controller(ActionBarCtrl, {
$scope: scope, $scope: scope,
email: emailMock, email: emailMock,
dialog: dialogMock, dialog: dialogMock,
statusDisplay: statusDisplayMock status: statusMock
}); });
}); });
}); });
@ -60,8 +52,8 @@ describe('Action Bar Controller unit test', function() {
scope.deleteMessage({}); scope.deleteMessage({});
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
expect(emailMock.deleteMessage.calledOnce).to.be.true; expect(emailMock.deleteMessage.calledOnce).to.be.true;
expect(scope.state.read.open).to.be.false;
}); });
}); });
@ -92,8 +84,8 @@ describe('Action Bar Controller unit test', function() {
scope.moveMessage({}, {}); scope.moveMessage({}, {});
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
expect(emailMock.moveMessage.calledOnce).to.be.true; expect(emailMock.moveMessage.calledOnce).to.be.true;
expect(scope.state.read.open).to.be.false;
}); });
}); });
@ -157,8 +149,17 @@ describe('Action Bar Controller unit test', function() {
scope.markMessage({}, true); scope.markMessage({}, true);
expect(statusMock.setReading.withArgs(false).calledOnce).to.be.true;
expect(emailMock.setFlags.calledOnce).to.be.true;
});
it('should mark the selected mail and close read mode', function() {
emailMock.setFlags.yields();
scope.markMessage({}, true, true);
expect(statusMock.setReading.calledOnce).to.be.false;
expect(emailMock.setFlags.calledOnce).to.be.true; expect(emailMock.setFlags.calledOnce).to.be.true;
expect(scope.state.read.open).to.be.false;
}); });
}); });

View File

@ -3,34 +3,15 @@
var MailListCtrl = require('../../../../src/js/controller/app/mail-list'), var MailListCtrl = require('../../../../src/js/controller/app/mail-list'),
EmailDAO = require('../../../../src/js/email/email'), EmailDAO = require('../../../../src/js/email/email'),
KeychainDAO = require('../../../../src/js/service/keychain'), KeychainDAO = require('../../../../src/js/service/keychain'),
StatusDisplay = require('../../../../src/js/util/status-display'), Status = require('../../../../src/js/util/status'),
Dialog = require('../../../../src/js/util/dialog'), Dialog = require('../../../../src/js/util/dialog'),
Search = require('../../../../src/js/email/search'); Search = require('../../../../src/js/email/search');
describe('Mail List controller unit test', function() { describe('Mail List controller unit test', function() {
var scope, ctrl, statusDisplayMock, notificationMock, emailMock, keychainMock, dialogMock, searchMock, var scope, ctrl, statusMock, notificationMock, emailMock, keychainMock, dialogMock, searchMock,
emailAddress, emails, emailAddress, emails, location;
hasChrome, hasSocket, hasRuntime, hasIdentity;
beforeEach(function() { beforeEach(function() {
hasChrome = !!window.chrome;
hasSocket = !!window.chrome.socket;
hasIdentity = !!window.chrome.identity;
if (!hasChrome) {
window.chrome = {};
}
if (!hasSocket) {
window.chrome.socket = {};
}
if (!hasRuntime) {
window.chrome.runtime = {
getURL: function() {}
};
}
if (!hasIdentity) {
window.chrome.identity = {};
}
emails = [{ emails = [{
unread: true unread: true
}, { }, {
@ -46,7 +27,7 @@ describe('Mail List controller unit test', function() {
close: function() {} close: function() {}
}; };
statusDisplayMock = sinon.createStubInstance(StatusDisplay); statusMock = sinon.createStubInstance(Status);
emailMock = sinon.createStubInstance(EmailDAO); emailMock = sinon.createStubInstance(EmailDAO);
keychainMock = sinon.createStubInstance(KeychainDAO); keychainMock = sinon.createStubInstance(KeychainDAO);
dialogMock = sinon.createStubInstance(Dialog); dialogMock = sinon.createStubInstance(Dialog);
@ -54,19 +35,16 @@ describe('Mail List controller unit test', function() {
angular.module('maillisttest', ['woEmail', 'woServices', 'woUtil']); angular.module('maillisttest', ['woEmail', 'woServices', 'woUtil']);
angular.mock.module('maillisttest'); angular.mock.module('maillisttest');
angular.mock.inject(function($rootScope, $controller) { angular.mock.inject(function($rootScope, $controller, $location) {
scope = $rootScope.$new(); scope = $rootScope.$new();
scope.state = { location = $location;
read: { scope.state = {};
toggle: function() {}
}
};
scope.loadVisibleBodies = function() {}; scope.loadVisibleBodies = function() {};
ctrl = $controller(MailListCtrl, { ctrl = $controller(MailListCtrl, {
$scope: scope, $scope: scope,
$routeParams: {}, $location: location,
statusDisplay: statusDisplayMock, status: statusMock,
notification: notificationMock, notification: notificationMock,
email: emailMock, email: emailMock,
keychain: keychainMock, keychain: keychainMock,
@ -76,20 +54,7 @@ describe('Mail List controller unit test', function() {
}); });
}); });
afterEach(function() { afterEach(function() {});
if (!hasSocket) {
delete window.chrome.socket;
}
if (!hasRuntime) {
delete window.chrome.runtime;
}
if (!hasChrome) {
delete window.chrome;
}
if (!hasIdentity) {
delete window.chrome.identity;
}
});
describe('displayMore', function() { describe('displayMore', function() {
beforeEach(function() { beforeEach(function() {
@ -132,21 +97,21 @@ describe('Mail List controller unit test', function() {
it('should show initial message on empty', function() { it('should show initial message on empty', function() {
scope.displaySearchResults(); scope.displaySearchResults();
expect(statusDisplayMock.setSearching.withArgs(false).calledOnce).to.be.true; expect(statusMock.setSearching.withArgs(false).calledOnce).to.be.true;
expect(statusDisplayMock.update.withArgs('Online').calledOnce).to.be.true; expect(statusMock.update.withArgs('Online').calledOnce).to.be.true;
expect(scope.displayMessages.length).to.equal(2); expect(scope.displayMessages.length).to.equal(2);
}); });
it('should show initial message on empty', function() { it('should show initial message on empty', function() {
searchMock.filter.returns(['a']); searchMock.filter.returns(['a']);
scope.displaySearchResults('query'); scope.displaySearchResults('query');
expect(statusDisplayMock.setSearching.withArgs(true).calledOnce).to.be.true; expect(statusMock.setSearching.withArgs(true).calledOnce).to.be.true;
expect(statusDisplayMock.update.withArgs('Searching ...').calledOnce).to.be.true; expect(statusMock.update.withArgs('Searching ...').calledOnce).to.be.true;
clock.tick(500); clock.tick(500);
expect(scope.displayMessages).to.deep.equal(['a']); expect(scope.displayMessages).to.deep.equal(['a']);
expect(statusDisplayMock.setSearching.withArgs(false).calledOnce).to.be.true; expect(statusMock.setSearching.withArgs(false).calledOnce).to.be.true;
expect(statusDisplayMock.update.withArgs('Matches in this folder').calledOnce).to.be.true; expect(statusMock.update.withArgs('Matches in this folder').calledOnce).to.be.true;
}); });
}); });
@ -160,10 +125,12 @@ describe('Mail List controller unit test', function() {
describe('push notification', function() { describe('push notification', function() {
beforeEach(function() { beforeEach(function() {
scope._stopWatchTask(); scope._stopWatchTask();
sinon.stub(scope, 'navigate');
}); });
afterEach(function() { afterEach(function() {
notificationMock.create.restore(); notificationMock.create.restore();
scope.navigate.restore();
}); });
it('should succeed for single mail', function(done) { it('should succeed for single mail', function(done) {
@ -181,7 +148,7 @@ describe('Mail List controller unit test', function() {
expect(opts.message).to.equal(mail.subject); expect(opts.message).to.equal(mail.subject);
opts.onClick(); opts.onClick();
expect(scope.state.mailList.selected).to.equal(mail); expect(scope.navigate.withArgs(mail).calledOnce).to.be.true;
done(); done();
}); });
@ -224,7 +191,7 @@ describe('Mail List controller unit test', function() {
expect(opts.message).to.equal(mails[0].subject + '\n' + mails[1].subject); expect(opts.message).to.equal(mails[0].subject + '\n' + mails[1].subject);
opts.onClick(); opts.onClick();
expect(scope.state.mailList.selected).to.equal(mails[0]); expect(scope.navigate.withArgs(mails[0]).calledOnce).to.be.true;
done(); done();
}); });
@ -358,7 +325,7 @@ describe('Mail List controller unit test', function() {
})); }));
it('should output date only if date is not today', angular.mock.inject(function($filter) { it('should output date only if date is not today', angular.mock.inject(function($filter) {
var yesterday = new Date(); var yesterday = new Date();
yesterday.setTime(yesterday.getTime() - 24*60*60*1000); yesterday.setTime(yesterday.getTime() - 24 * 60 * 60 * 1000);
var expected = $filter('date')(yesterday, 'mediumDate'); var expected = $filter('date')(yesterday, 'mediumDate');
expect(scope.formatDate(yesterday)).to.equal(expected); expect(scope.formatDate(yesterday)).to.equal(expected);

View File

@ -119,7 +119,7 @@ describe('Login Controller unit test', function() {
expect(dialogMock.error.calledOnce).to.be.true; expect(dialogMock.error.calledOnce).to.be.true;
}); });
it('should redirect to /desktop', function() { it('should redirect to /account', function() {
authMock.init.yields(); authMock.init.yields();
authMock.getEmailAddress.yields(null, { authMock.getEmailAddress.yields(null, {
emailAddress: emailAddress emailAddress: emailAddress
@ -133,7 +133,7 @@ describe('Login Controller unit test', function() {
createController(); createController();
expect(goToStub.withArgs('/desktop').calledOnce).to.be.true; expect(goToStub.withArgs('/account').calledOnce).to.be.true;
}); });
it('should fail for keychain.requestPrivateKeyDownload', function() { it('should fail for keychain.requestPrivateKeyDownload', function() {

View File

@ -60,7 +60,7 @@ describe('Login (existing user) Controller unit test', function() {
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true; expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true; expect(emailDaoMock.unlock.calledOnce).to.be.true;
expect(pathSpy.calledOnce).to.be.true; expect(pathSpy.calledOnce).to.be.true;
expect(pathSpy.calledWith('/desktop')).to.be.true; expect(pathSpy.calledWith('/account')).to.be.true;
}); });
it('should not work when keypair unavailable', function() { it('should not work when keypair unavailable', function() {

View File

@ -102,7 +102,7 @@ describe('Login (initial user) Controller unit test', function() {
expect(scope.errMsg).to.not.exist; expect(scope.errMsg).to.not.exist;
expect(scope.state.ui).to.equal(2); expect(scope.state.ui).to.equal(2);
expect(newsletterStub.called).to.be.true; expect(newsletterStub.called).to.be.true;
expect(location.$$path).to.equal('/desktop'); expect(location.$$path).to.equal('/account');
expect(emailMock.unlock.calledOnce).to.be.true; expect(emailMock.unlock.calledOnce).to.be.true;
}); });
}); });

View File

@ -152,7 +152,7 @@ describe('Login Private Key Download Controller unit test', function() {
scope.decryptAndStorePrivateKeyLocally(); scope.decryptAndStorePrivateKeyLocally();
}); });
it('should goto /desktop on emailDao.unlock success', function(done) { it('should goto /account on emailDao.unlock success', function(done) {
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, { keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
encryptedKey: 'keyArmored' encryptedKey: 'keyArmored'
}); });
@ -160,7 +160,7 @@ describe('Login Private Key Download Controller unit test', function() {
authMock.storeCredentials.yields(); authMock.storeCredentials.yields();
scope.goTo = function(location) { scope.goTo = function(location) {
expect(location).to.equal('/desktop'); expect(location).to.equal('/account');
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true; expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
expect(emailDaoMock.unlock.calledOnce).to.be.true; expect(emailDaoMock.unlock.calledOnce).to.be.true;
done(); done();
@ -173,10 +173,10 @@ describe('Login Private Key Download Controller unit test', function() {
describe('goTo', function() { describe('goTo', function() {
it('should work', function() { it('should work', function() {
sinon.stub(location, 'path', function(path) { sinon.stub(location, 'path', function(path) {
expect(path).to.equal('/desktop'); expect(path).to.equal('/account');
}); });
scope.goTo('/desktop'); scope.goTo('/account');
}); });
}); });
}); });

View File

@ -1,64 +0,0 @@
'use strict';
var btnHandler = require('../../../src/js/util/backbutton-handler');
describe('Backbutton Handler', function() {
chai.config.includeStack = true;
var scope, event;
beforeEach(function() {
scope = {
state: {},
$apply: function() {}
};
event = new CustomEvent('backbutton');
// this is a precondition for the test. throw an exception
// if this would produce side effects
expect(navigator.app).to.not.exist;
navigator.app = {};
btnHandler.attachHandler(scope);
btnHandler.start();
});
afterEach(function() {
btnHandler.stop();
delete navigator.app;
});
it('should close lightbox', function() {
scope.state.lightbox = 'asd';
document.dispatchEvent(event);
expect(scope.state.lightbox).to.be.undefined;
});
it('should close reader', function() {
scope.state.read = {
open: true,
toggle: function(state) {
scope.state.read.open = state;
}
};
document.dispatchEvent(event);
expect(scope.state.read.open).to.be.false;
});
it('should close navigation', function() {
scope.state.nav = {
open: true,
toggle: function(state) {
scope.state.nav.open = state;
}
};
document.dispatchEvent(event);
expect(scope.state.nav.open).to.be.false;
});
it('should close app', function(done) {
navigator.app.exitApp = done;
document.dispatchEvent(event);
});
});

View File

@ -1,14 +1,14 @@
'use strict'; 'use strict';
describe('Status Display Service unit test', function() { describe('Status Service unit test', function() {
var statusDisplay, logInfoStub, rootScope, broadcastSpy; var status, logInfoStub, rootScope, broadcastSpy;
beforeEach(function() { beforeEach(function() {
angular.module('statusDisplay-test', ['woUtil']); angular.module('status-test', ['woUtil']);
angular.mock.module('statusDisplay-test'); angular.mock.module('status-test');
angular.mock.inject(function($injector, axe) { angular.mock.inject(function($injector, axe) {
logInfoStub = sinon.stub(axe, 'info'); logInfoStub = sinon.stub(axe, 'info');
statusDisplay = $injector.get('statusDisplay'); status = $injector.get('status');
rootScope = $injector.get('$rootScope'); rootScope = $injector.get('$rootScope');
broadcastSpy = sinon.spy(rootScope, '$broadcast'); broadcastSpy = sinon.spy(rootScope, '$broadcast');
}); });
@ -23,7 +23,7 @@ describe('Status Display Service unit test', function() {
var message = 'Tada!', var message = 'Tada!',
time = new Date(); time = new Date();
statusDisplay.update(message, time); status.update(message, time);
expect(broadcastSpy.withArgs('status', message, time).calledOnce).to.be.true; expect(broadcastSpy.withArgs('status', message, time).calledOnce).to.be.true;
expect(logInfoStub.withArgs('status display', message).calledOnce).to.be.true; expect(logInfoStub.withArgs('status display', message).calledOnce).to.be.true;
@ -32,10 +32,18 @@ describe('Status Display Service unit test', function() {
describe('setSearching', function() { describe('setSearching', function() {
it('should work', function() { it('should work', function() {
statusDisplay.setSearching(true); status.setSearching(true);
expect(broadcastSpy.withArgs('searching', true).calledOnce).to.be.true; expect(broadcastSpy.withArgs('searching', true).calledOnce).to.be.true;
}); });
}); });
describe('setReading', function() {
it('should work', function() {
status.setReading(true);
expect(broadcastSpy.withArgs('read', true).calledOnce).to.be.true;
});
});
}); });