mirror of
https://github.com/moparisthebest/mail
synced 2024-11-26 02:42:17 -05:00
[WO-649] clean up login pages
* add spinners to all login pages * use inline error messages in all form instead of scope.onError * create newsletter service
This commit is contained in:
parent
082cbf192b
commit
cf1f60fbf9
@ -169,6 +169,8 @@ module.exports = function(grunt) {
|
|||||||
'test/unit/lawnchair-dao-test.js',
|
'test/unit/lawnchair-dao-test.js',
|
||||||
'test/unit/keychain-dao-test.js',
|
'test/unit/keychain-dao-test.js',
|
||||||
'test/unit/devicestorage-dao-test.js',
|
'test/unit/devicestorage-dao-test.js',
|
||||||
|
'test/unit/newsletter-service-test.js',
|
||||||
|
'test/unit/mail-config-service-test.js',
|
||||||
'test/unit/dialog-ctrl-test.js',
|
'test/unit/dialog-ctrl-test.js',
|
||||||
'test/unit/add-account-ctrl-test.js',
|
'test/unit/add-account-ctrl-test.js',
|
||||||
'test/unit/create-account-ctrl-test.js',
|
'test/unit/create-account-ctrl-test.js',
|
||||||
@ -192,7 +194,6 @@ module.exports = function(grunt) {
|
|||||||
'test/unit/invitation-dao-test.js',
|
'test/unit/invitation-dao-test.js',
|
||||||
'test/unit/update-handler-test.js',
|
'test/unit/update-handler-test.js',
|
||||||
'test/unit/connection-doctor-test.js',
|
'test/unit/connection-doctor-test.js',
|
||||||
'test/unit/mail-config-service-test.js',
|
|
||||||
'test/main.js'
|
'test/main.js'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -37,6 +37,7 @@ var DialogCtrl = require('./controller/dialog'),
|
|||||||
errorUtil = require('./util/error'),
|
errorUtil = require('./util/error'),
|
||||||
backButtonUtil = require('./util/backbutton-handler');
|
backButtonUtil = require('./util/backbutton-handler');
|
||||||
require('./directive/common'),
|
require('./directive/common'),
|
||||||
|
require('./service/newsletter'),
|
||||||
require('./service/mail-config');
|
require('./service/mail-config');
|
||||||
|
|
||||||
// init main angular module including dependencies
|
// init main angular module including dependencies
|
||||||
|
@ -10,21 +10,16 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
|||||||
|
|
||||||
var emailDao = appController._emailDao;
|
var emailDao = appController._emailDao;
|
||||||
|
|
||||||
$scope.buttonEnabled = true;
|
|
||||||
$scope.incorrect = false;
|
|
||||||
|
|
||||||
$scope.change = function() {
|
|
||||||
$scope.incorrect = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.confirmPassphrase = function() {
|
$scope.confirmPassphrase = function() {
|
||||||
if (!$scope.passphrase) {
|
if ($scope.form.$invalid) {
|
||||||
|
$scope.errMsg = 'Please fill out all required fields!';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable button once loggin has started
|
$scope.busy = true;
|
||||||
$scope.buttonEnabled = false;
|
$scope.errMsg = undefined;
|
||||||
$scope.incorrect = false;
|
$scope.incorrect = false;
|
||||||
|
|
||||||
unlockCrypto();
|
unlockCrypto();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -32,7 +27,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
|||||||
var userId = emailDao._account.emailAddress;
|
var userId = emailDao._account.emailAddress;
|
||||||
emailDao._keychain.getUserKeyPair(userId, function(err, keypair) {
|
emailDao._keychain.getUserKeyPair(userId, function(err, keypair) {
|
||||||
if (err) {
|
if (err) {
|
||||||
handleError(err);
|
displayError(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,15 +40,14 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
|||||||
|
|
||||||
function onUnlock(err) {
|
function onUnlock(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
$scope.incorrect = true;
|
displayError(err);
|
||||||
$scope.buttonEnabled = true;
|
|
||||||
$scope.$apply();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
appController._auth.storeCredentials(function(err) {
|
appController._auth.storeCredentials(function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return $scope.onError(err);
|
displayError(err);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$location.path('/desktop');
|
$location.path('/desktop');
|
||||||
@ -61,10 +55,11 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleError(err) {
|
function displayError(err) {
|
||||||
|
$scope.busy = false;
|
||||||
$scope.incorrect = true;
|
$scope.incorrect = true;
|
||||||
$scope.buttonEnabled = true;
|
$scope.errMsg = err.errMsg || err.message;
|
||||||
$scope.onError(err);
|
$scope.$apply();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,20 +2,24 @@
|
|||||||
|
|
||||||
var appController = require('../app-controller');
|
var appController = require('../app-controller');
|
||||||
|
|
||||||
var LoginInitialCtrl = function($scope, $location, $routeParams) {
|
var LoginInitialCtrl = function($scope, $location, $routeParams, newsletter) {
|
||||||
if (!appController._emailDao && !$routeParams.dev) {
|
if (!appController._emailDao && !$routeParams.dev) {
|
||||||
$location.path('/'); // init app
|
$location.path('/'); // init app
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var emailDao = appController._emailDao,
|
if (appController._emailDao) {
|
||||||
states, termsMsg = 'You must accept the Terms of Service to continue.';
|
var emailDao = appController._emailDao,
|
||||||
|
emailAddress = emailDao._account.emailAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
var termsMsg = 'You must accept the Terms of Service to continue.',
|
||||||
|
states = {
|
||||||
|
IDLE: 1,
|
||||||
|
PROCESSING: 2,
|
||||||
|
DONE: 3
|
||||||
|
};
|
||||||
|
|
||||||
states = {
|
|
||||||
IDLE: 1,
|
|
||||||
PROCESSING: 2,
|
|
||||||
DONE: 3
|
|
||||||
};
|
|
||||||
$scope.state.ui = states.IDLE; // initial state
|
$scope.state.ui = states.IDLE; // initial state
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -26,15 +30,15 @@ var LoginInitialCtrl = function($scope, $location, $routeParams) {
|
|||||||
* Continue to key import screen
|
* Continue to key import screen
|
||||||
*/
|
*/
|
||||||
$scope.importKey = function() {
|
$scope.importKey = function() {
|
||||||
if (!$scope.state.agree) {
|
if (!$scope.agree) {
|
||||||
$scope.onError({
|
displayError(new Error(termsMsg));
|
||||||
message: termsMsg
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.errMsg = undefined;
|
||||||
|
|
||||||
// sing up to newsletter
|
// sing up to newsletter
|
||||||
$scope.signUpToNewsletter();
|
newsletter.signup(emailAddress, $scope.newsletter);
|
||||||
// go to key import
|
// go to key import
|
||||||
$location.path('/login-new-device');
|
$location.path('/login-new-device');
|
||||||
};
|
};
|
||||||
@ -43,77 +47,47 @@ var LoginInitialCtrl = function($scope, $location, $routeParams) {
|
|||||||
* Continue to keygen
|
* Continue to keygen
|
||||||
*/
|
*/
|
||||||
$scope.generateKey = function() {
|
$scope.generateKey = function() {
|
||||||
if (!$scope.state.agree) {
|
if (!$scope.agree) {
|
||||||
$scope.onError({
|
displayError(new Error(termsMsg));
|
||||||
message: termsMsg
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.errMsg = undefined;
|
||||||
|
|
||||||
// sing up to newsletter
|
// sing up to newsletter
|
||||||
$scope.signUpToNewsletter();
|
newsletter.signup(emailAddress, $scope.newsletter);
|
||||||
// go to set keygen screen
|
// go to set keygen screen
|
||||||
$scope.setState(states.PROCESSING);
|
$scope.setState(states.PROCESSING);
|
||||||
|
|
||||||
setTimeout(function() {
|
emailDao.unlock({
|
||||||
emailDao.unlock({
|
passphrase: undefined // generate key without passphrase
|
||||||
passphrase: undefined // generate key without passphrase
|
}, function(err) {
|
||||||
}, function(err) {
|
if (err) {
|
||||||
|
displayError(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
appController._auth.storeCredentials(function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
$scope.setState(states.IDLE);
|
displayError(err);
|
||||||
$scope.onError(err);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
appController._auth.storeCredentials(function(err) {
|
$location.path('/desktop');
|
||||||
if (err) {
|
$scope.$apply();
|
||||||
return $scope.onError(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
$location.path('/desktop');
|
|
||||||
$scope.$apply();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}, 500);
|
});
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [signUpToNewsletter description]
|
|
||||||
* @param {Function} callback (optional)
|
|
||||||
*/
|
|
||||||
$scope.signUpToNewsletter = function(callback) {
|
|
||||||
if (!$scope.state.newsletter) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var address = emailDao._account.emailAddress;
|
|
||||||
var uri = 'https://whiteout.us8.list-manage.com/subscribe/post?u=52ea5a9e1be9e1d194f184158&id=6538e8f09f';
|
|
||||||
|
|
||||||
var formData = new FormData();
|
|
||||||
formData.append('EMAIL', address);
|
|
||||||
formData.append('b_52ea5a9e1be9e1d194f184158_6538e8f09f', '');
|
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.open('post', uri, true);
|
|
||||||
|
|
||||||
xhr.onload = function() {
|
|
||||||
if (callback) {
|
|
||||||
callback(null, xhr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.onerror = function(err) {
|
|
||||||
if (callback) {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.send(formData);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.setState = function(state) {
|
$scope.setState = function(state) {
|
||||||
$scope.state.ui = state;
|
$scope.state.ui = state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function displayError(err) {
|
||||||
|
$scope.setState(states.IDLE);
|
||||||
|
$scope.errMsg = err.errMsg || err.message;
|
||||||
|
$scope.$apply();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = LoginInitialCtrl;
|
module.exports = LoginInitialCtrl;
|
@ -14,7 +14,15 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
|||||||
$scope.incorrect = false;
|
$scope.incorrect = false;
|
||||||
|
|
||||||
$scope.confirmPassphrase = function() {
|
$scope.confirmPassphrase = function() {
|
||||||
|
if ($scope.form.$invalid || !$scope.key) {
|
||||||
|
$scope.errMsg = 'Please fill out all required fields!';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.busy = true;
|
||||||
|
$scope.errMsg = undefined; // reset error msg
|
||||||
$scope.incorrect = false;
|
$scope.incorrect = false;
|
||||||
|
|
||||||
unlockCrypto();
|
unlockCrypto();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,7 +31,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
|||||||
// check if user already has a public key on the key server
|
// check if user already has a public key on the key server
|
||||||
emailDao._keychain.getUserKeyPair(userId, function(err, keypair) {
|
emailDao._keychain.getUserKeyPair(userId, function(err, keypair) {
|
||||||
if (err) {
|
if (err) {
|
||||||
$scope.onError(err);
|
$scope.displayError(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +42,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
|||||||
try {
|
try {
|
||||||
$scope.key.publicKeyArmored = pgp.extractPublicKey($scope.key.privateKeyArmored);
|
$scope.key.publicKeyArmored = pgp.extractPublicKey($scope.key.privateKeyArmored);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$scope.onError(new Error('Error parsing public key from private key!'));
|
$scope.displayError(new Error('Error reading PGP key!'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +53,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
|||||||
privKeyParams = pgp.getKeyParams($scope.key.privateKeyArmored);
|
privKeyParams = pgp.getKeyParams($scope.key.privateKeyArmored);
|
||||||
pubKeyParams = pgp.getKeyParams($scope.key.publicKeyArmored);
|
pubKeyParams = pgp.getKeyParams($scope.key.publicKeyArmored);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$scope.onError(new Error('Error reading key params!'));
|
$scope.displayError(new Error('Error reading key params!'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +82,7 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
|||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
$scope.incorrect = true;
|
$scope.incorrect = true;
|
||||||
$scope.onError(err);
|
$scope.displayError(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,19 +93,26 @@ var LoginExistingCtrl = function($scope, $location, $routeParams) {
|
|||||||
|
|
||||||
function onUnlock(err) {
|
function onUnlock(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
$scope.onError(err);
|
$scope.displayError(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
appController._auth.storeCredentials(function(err) {
|
appController._auth.storeCredentials(function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return $scope.onError(err);
|
$scope.displayError(err);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$location.path('/desktop');
|
$location.path('/desktop');
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.displayError = function(err) {
|
||||||
|
$scope.busy = false;
|
||||||
|
$scope.errMsg = err.errMsg || err.message;
|
||||||
|
$scope.$apply();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var ngModule = angular.module('login-new-device', []);
|
var ngModule = angular.module('login-new-device', []);
|
||||||
@ -117,7 +132,7 @@ ngModule.directive('fileReader', function() {
|
|||||||
keyParts;
|
keyParts;
|
||||||
|
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
scope.onError(new Error('Error parsing private PGP key block!'));
|
scope.displayError(new Error('Error parsing private PGP key block!'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,12 +8,116 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var keychain = appController._keychain,
|
if (appController._emailDao) {
|
||||||
emailDao = appController._emailDao,
|
var keychain = appController._keychain,
|
||||||
userId = emailDao._account.emailAddress;
|
emailDao = appController._emailDao,
|
||||||
|
userId = emailDao._account.emailAddress;
|
||||||
|
}
|
||||||
|
|
||||||
$scope.step = 1;
|
$scope.step = 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Token
|
||||||
|
//
|
||||||
|
|
||||||
|
$scope.checkToken = function() {
|
||||||
|
if ($scope.tokenForm.$invalid) {
|
||||||
|
$scope.errMsg = 'Please enter a valid recovery token!';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.busy = true;
|
||||||
|
$scope.errMsg = undefined;
|
||||||
|
|
||||||
|
$scope.verifyRecoveryToken(function() {
|
||||||
|
$scope.busy = false;
|
||||||
|
$scope.errMsg = undefined;
|
||||||
|
$scope.step++;
|
||||||
|
$scope.$apply();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.verifyRecoveryToken = function(callback) {
|
||||||
|
keychain.getUserKeyPair(userId, function(err, keypair) {
|
||||||
|
if (err) {
|
||||||
|
displayError(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember for storage later
|
||||||
|
$scope.cachedKeypair = keypair;
|
||||||
|
|
||||||
|
keychain.downloadPrivateKey({
|
||||||
|
userId: userId,
|
||||||
|
keyId: keypair.publicKey._id,
|
||||||
|
recoveryToken: $scope.recoveryToken.toUpperCase()
|
||||||
|
}, function(err, encryptedPrivateKey) {
|
||||||
|
if (err) {
|
||||||
|
displayError(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.encryptedPrivateKey = encryptedPrivateKey;
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Keychain code
|
||||||
|
//
|
||||||
|
|
||||||
|
$scope.checkCode = function() {
|
||||||
|
if ($scope.codeForm.$invalid) {
|
||||||
|
$scope.errMsg = 'Please fill out all required fields!';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.busy = true;
|
||||||
|
$scope.errMsg = undefined;
|
||||||
|
|
||||||
|
$scope.decryptAndStorePrivateKeyLocally();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.decryptAndStorePrivateKeyLocally = function() {
|
||||||
|
var inputCode = '' + $scope.code0 + $scope.code1 + $scope.code2 + $scope.code3 + $scope.code4 + $scope.code5;
|
||||||
|
|
||||||
|
var options = $scope.encryptedPrivateKey;
|
||||||
|
options.code = inputCode.toUpperCase();
|
||||||
|
|
||||||
|
keychain.decryptAndStorePrivateKeyLocally(options, function(err, privateKey) {
|
||||||
|
if (err) {
|
||||||
|
displayError(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add private key to cached keypair object
|
||||||
|
$scope.cachedKeypair.privateKey = privateKey;
|
||||||
|
|
||||||
|
// try empty passphrase
|
||||||
|
emailDao.unlock({
|
||||||
|
keypair: $scope.cachedKeypair,
|
||||||
|
passphrase: undefined
|
||||||
|
}, function(err) {
|
||||||
|
if (err) {
|
||||||
|
// go to passphrase login screen
|
||||||
|
$scope.goTo('/login-existing');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// passphrase is corrent ... go to main app
|
||||||
|
appController._auth.storeCredentials(function(err) {
|
||||||
|
if (err) {
|
||||||
|
displayError(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.goTo('/desktop');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
$scope.handlePaste = function(event) {
|
$scope.handlePaste = function(event) {
|
||||||
var evt = event;
|
var evt = event;
|
||||||
if (evt.originalEvent) {
|
if (evt.originalEvent) {
|
||||||
@ -34,99 +138,21 @@ var LoginPrivateKeyDownloadCtrl = function($scope, $location, $routeParams) {
|
|||||||
$scope.code5 = value.slice(20, 24);
|
$scope.code5 = value.slice(20, 24);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.verifyRecoveryToken = function(callback) {
|
//
|
||||||
if (!$scope.recoveryToken) {
|
// helper functions
|
||||||
$scope.onError(new Error('Please set the recovery token!'));
|
//
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
keychain.getUserKeyPair(userId, function(err, keypair) {
|
|
||||||
if (err) {
|
|
||||||
$scope.onError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remember for storage later
|
|
||||||
$scope.cachedKeypair = keypair;
|
|
||||||
|
|
||||||
keychain.downloadPrivateKey({
|
|
||||||
userId: userId,
|
|
||||||
keyId: keypair.publicKey._id,
|
|
||||||
recoveryToken: $scope.recoveryToken.toUpperCase()
|
|
||||||
}, function(err, encryptedPrivateKey) {
|
|
||||||
if (err) {
|
|
||||||
$scope.onError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.encryptedPrivateKey = encryptedPrivateKey;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.decryptAndStorePrivateKeyLocally = function() {
|
|
||||||
var inputCode = '' + $scope.code0 + $scope.code1 + $scope.code2 + $scope.code3 + $scope.code4 + $scope.code5;
|
|
||||||
|
|
||||||
if (!inputCode) {
|
|
||||||
$scope.onError(new Error('Please enter the keychain code!'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = $scope.encryptedPrivateKey;
|
|
||||||
options.code = inputCode.toUpperCase();
|
|
||||||
|
|
||||||
keychain.decryptAndStorePrivateKeyLocally(options, function(err, privateKey) {
|
|
||||||
if (err) {
|
|
||||||
$scope.onError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add private key to cached keypair object
|
|
||||||
$scope.cachedKeypair.privateKey = privateKey;
|
|
||||||
|
|
||||||
// try empty passphrase
|
|
||||||
emailDao.unlock({
|
|
||||||
keypair: $scope.cachedKeypair,
|
|
||||||
passphrase: undefined
|
|
||||||
}, function(err) {
|
|
||||||
if (err) {
|
|
||||||
// go to passphrase login screen
|
|
||||||
$scope.goTo('/login-existing');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// passphrase is corrent ... go to main app
|
|
||||||
appController._auth.storeCredentials(function(err) {
|
|
||||||
if (err) {
|
|
||||||
return $scope.onError(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.goTo('/desktop');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.goForward = function() {
|
|
||||||
if ($scope.step === 1) {
|
|
||||||
$scope.verifyRecoveryToken(function() {
|
|
||||||
$scope.step++;
|
|
||||||
$scope.$apply();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($scope.step === 2) {
|
|
||||||
$scope.decryptAndStorePrivateKeyLocally();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.goTo = function(location) {
|
$scope.goTo = function(location) {
|
||||||
$location.path(location);
|
$location.path(location);
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function displayError(err) {
|
||||||
|
$scope.busy = false;
|
||||||
|
$scope.incorrect = true;
|
||||||
|
$scope.errMsg = err.errMsg || err.message;
|
||||||
|
$scope.$apply();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = LoginPrivateKeyDownloadCtrl;
|
module.exports = LoginPrivateKeyDownloadCtrl;
|
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var ngModule = angular.module('woServices', []);
|
var ngModule = angular.module('woServices');
|
||||||
ngModule.service('mailConfig', MailConfig);
|
ngModule.service('mailConfig', MailConfig);
|
||||||
|
|
||||||
var cfg = require('../app-config').config;
|
var cfg = require('../app-config').config;
|
||||||
|
45
src/js/service/newsletter.js
Normal file
45
src/js/service/newsletter.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ngModule = angular.module('woServices', []);
|
||||||
|
ngModule.service('newsletter', Newsletter);
|
||||||
|
|
||||||
|
function Newsletter($q) {
|
||||||
|
this._q = $q;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign up to the whiteout newsletter
|
||||||
|
*/
|
||||||
|
Newsletter.prototype.signup = function(emailAddress, agree) {
|
||||||
|
return this._q(function(resolve, reject) {
|
||||||
|
// validate email address
|
||||||
|
if (emailAddress.indexOf('@') < 0) {
|
||||||
|
reject(new Error('Invalid email address!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!agree) {
|
||||||
|
// don't sign up if the user has not agreed
|
||||||
|
resolve(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append('EMAIL', emailAddress);
|
||||||
|
formData.append('b_52ea5a9e1be9e1d194f184158_6538e8f09f', '');
|
||||||
|
|
||||||
|
var uri = 'https://whiteout.us8.list-manage.com/subscribe/post?u=52ea5a9e1be9e1d194f184158&id=6538e8f09f';
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('post', uri, true);
|
||||||
|
|
||||||
|
xhr.onload = function() {
|
||||||
|
resolve(xhr);
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = function(err) {
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send(formData);
|
||||||
|
});
|
||||||
|
};
|
@ -3,6 +3,8 @@
|
|||||||
.page {
|
.page {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
// allow scrolling on iOS
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
// disable text selection
|
// disable text selection
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
@ -6,17 +6,19 @@
|
|||||||
<main class="page__main">
|
<main class="page__main">
|
||||||
<h2 class="typo-title">Unlock mailbox</h2>
|
<h2 class="typo-title">Unlock mailbox</h2>
|
||||||
<p class="typo-paragraph">Please enter your passphrase to unlock the mailbox.</p>
|
<p class="typo-paragraph">Please enter your passphrase to unlock the mailbox.</p>
|
||||||
<form class="form">
|
<form class="form" name="form">
|
||||||
<!-- TODO add error messages -->
|
<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
||||||
<!--<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
|
||||||
<p class="form__error-message" ng-show="form.$invalid">Please fill out all required fields!</p>-->
|
|
||||||
|
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<input class="input-text input-text--big" type="password" ng-model="passphrase" ng-change="change()"
|
<input type="password" ng-model="passphrase"
|
||||||
ng-class="{'input-text--error':incorrect}" placeholder="Passphrase" tabindex="1" focus-me="true">
|
class="input-text input-text--big" ng-class="{'input-text--error':incorrect}"
|
||||||
|
placeholder="Passphrase" tabindex="1" focus-me="true" required>
|
||||||
|
</div>
|
||||||
|
<div class="spinner-block" ng-show="busy">
|
||||||
|
<span class="spinner spinner--big"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<button class="btn btn--big" type="submit" wo-touch="confirmPassphrase()" ng-disabled="!buttonEnabled" tabindex="2">
|
<button class="btn btn--big" type="submit" ng-click="confirmPassphrase()" tabindex="2">
|
||||||
<svg role="presentation"><use xlink:href="#icon-decrypted" /></svg>
|
<svg role="presentation"><use xlink:href="#icon-decrypted" /></svg>
|
||||||
Unlock
|
Unlock
|
||||||
</button>
|
</button>
|
||||||
|
@ -12,13 +12,11 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form class="form" name="form">
|
<form class="form" name="form">
|
||||||
<!-- TODO: remove error dialog and use inline error messages -->
|
<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
||||||
<!--<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
|
||||||
<p class="form__error-message" ng-show="form.$invalid">Please fill out all required fields!</p>-->
|
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<label class="input-checkbox">
|
<label class="input-checkbox">
|
||||||
<span class="checkbox">
|
<span class="checkbox">
|
||||||
<input type="checkbox" ng-model="state.agree">
|
<input type="checkbox" ng-model="agree">
|
||||||
<span class="input-checkbox__box"><svg role="presentation"><use xlink:href="#icon-check" /></svg></span>
|
<span class="input-checkbox__box"><svg role="presentation"><use xlink:href="#icon-check" /></svg></span>
|
||||||
</span>
|
</span>
|
||||||
I agree to the Whiteout Networks <a href="https://whiteout.io/terms.html" target="_blank">Terms of Service</a> and have read the <a href="https://whiteout.io/privacy-service.html" target="_blank">Privacy Policy</a>
|
I agree to the Whiteout Networks <a href="https://whiteout.io/terms.html" target="_blank">Terms of Service</a> and have read the <a href="https://whiteout.io/privacy-service.html" target="_blank">Privacy Policy</a>
|
||||||
@ -27,17 +25,17 @@
|
|||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<label class="input-checkbox">
|
<label class="input-checkbox">
|
||||||
<span class="checkbox">
|
<span class="checkbox">
|
||||||
<input type="checkbox" ng-model="state.newsletter">
|
<input type="checkbox" ng-model="newsletter">
|
||||||
<span class="input-checkbox__box"><svg role="presentation"><use xlink:href="#icon-check" /></svg></span>
|
<span class="input-checkbox__box"><svg role="presentation"><use xlink:href="#icon-check" /></svg></span>
|
||||||
</span>
|
</span>
|
||||||
Stay up to date on Whiteout Networks products and important announcements.
|
Stay up to date on Whiteout Networks products and important announcements.
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<button type="submit" wo-touch="generateKey()" class="btn" tabindex="3">Generate new key</button>
|
<button type="submit" ng-click="generateKey()" class="btn" tabindex="3">Generate new key</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<button type="button" wo-touch="importKey()" class="btn btn--secondary">Import existing key</button>
|
<button type="button" ng-click="importKey()" class="btn btn--secondary">Import existing key</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -47,7 +45,7 @@
|
|||||||
<p class="typo-paragraph">
|
<p class="typo-paragraph">
|
||||||
Please stand by. This can take a while…
|
Please stand by. This can take a while…
|
||||||
</p>
|
</p>
|
||||||
<div class="u-text-center">
|
<div class="spinner-block spinner-block--standalone">
|
||||||
<span class="spinner spinner--big"></span>
|
<span class="spinner spinner--big"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,23 +17,19 @@
|
|||||||
</p>
|
</p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<!-- TODO -->
|
<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
||||||
<!--<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
|
||||||
<p class="form__error-message" ng-show="form.$invalid">Please fill out all required fields!</p>-->
|
|
||||||
|
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<input class="input-file" type="file" accept=".asc" file-reader tabindex="1">
|
<input class="input-file" type="file" accept=".asc" file-reader tabindex="1" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<input class="input-text" type="password" ng-model="passphrase"
|
<input class="input-text" type="password" ng-model="passphrase"
|
||||||
ng-class="{'input-text--error':incorrect}" placeholder="Passphrase" tabindex="2">
|
ng-class="{'input-text--error':incorrect}" placeholder="Passphrase" tabindex="2">
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO -->
|
<div class="spinner-block" ng-show="busy">
|
||||||
<!--<div class="spinner-block" ng-show="busy">
|
|
||||||
<span class="spinner spinner--big"></span>
|
<span class="spinner spinner--big"></span>
|
||||||
</div>-->
|
</div>
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<button type="submit" wo-touch="confirmPassphrase()" class="btn" ng-disabled="!key" tabindex="3">Import</button>
|
<button type="submit" ng-click="confirmPassphrase()" class="btn" tabindex="3">Import</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<p class="typo-paragraph">
|
<p class="typo-paragraph">
|
||||||
|
@ -8,16 +8,18 @@
|
|||||||
<div ng-show="step === 1">
|
<div ng-show="step === 1">
|
||||||
<h2 class="typo-title">Key sync</h2>
|
<h2 class="typo-title">Key sync</h2>
|
||||||
<p class="typo-paragraph">We have sent you an email containing a recovery token. Please copy and paste the token below to download your key.</p>
|
<p class="typo-paragraph">We have sent you an email containing a recovery token. Please copy and paste the token below to download your key.</p>
|
||||||
<form class="form">
|
<form class="form" name="tokenForm">
|
||||||
<!-- TODO add error messages -->
|
<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
||||||
<!--<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
|
||||||
<p class="form__error-message" ng-show="form.$invalid">Please fill out all required fields!</p>-->
|
|
||||||
|
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<input type="text" class="input-text input-text--big" size="6" maxlength="6" ng-model="recoveryToken" placeholder="Token" focus-me="step === 1" pattern="([a-zA-Z0-9]*)">
|
<input type="text" class="input-text input-text--big" ng-class="{'input-text--error':incorrect}"
|
||||||
|
size="6" maxlength="6" ng-model="recoveryToken" placeholder="Token" focus-me="step === 1" pattern="([a-zA-Z0-9]*)" required>
|
||||||
|
</div>
|
||||||
|
<div class="spinner-block" ng-show="busy">
|
||||||
|
<span class="spinner spinner--big"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<button class="btn btn--big" wo-touch="goForward()">Confirm token</button>
|
<button class="btn btn--big" type="submit" ng-click="checkToken()">Confirm token</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@ -37,23 +39,24 @@
|
|||||||
<div ng-show="step === 2">
|
<div ng-show="step === 2">
|
||||||
<h2 class="typo-title">Key sync</h2>
|
<h2 class="typo-title">Key sync</h2>
|
||||||
<p class="typo-paragraph">Please enter the keychain code you wrote down during sync setup.</p>
|
<p class="typo-paragraph">Please enter the keychain code you wrote down during sync setup.</p>
|
||||||
<form class="form">
|
<form class="form" name="codeForm">
|
||||||
<!-- TODO add error messages -->
|
<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
||||||
<!--<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
|
||||||
<p class="form__error-message" ng-show="form.$invalid">Please fill out all required fields!</p>-->
|
|
||||||
|
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<div class="input-code">
|
<div class="input-code">
|
||||||
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code0" focus-me="step === 2" focus-next ng-paste="handlePaste($event)"> -
|
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code0" focus-me="step === 2" focus-next required ng-paste="handlePaste($event)"> -
|
||||||
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code1" focus-next> -
|
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code1" focus-next required> -
|
||||||
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code2" focus-next> -
|
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code2" focus-next required> -
|
||||||
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code3" focus-next> -
|
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code3" focus-next required> -
|
||||||
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code4" focus-next> -
|
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code4" focus-next required> -
|
||||||
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code5">
|
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code5" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="spinner-block" ng-show="busy">
|
||||||
|
<span class="spinner spinner--big"></span>
|
||||||
|
</div>
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<button class="btn" wo-touch="goForward()">Complete sync</button>
|
<button class="btn" type="submit" ng-click="checkCode()">Complete sync</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<p class="typo-paragraph">
|
<p class="typo-paragraph">
|
||||||
|
@ -108,7 +108,7 @@
|
|||||||
<span class="spinner spinner--big"></span>
|
<span class="spinner spinner--big"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<button type="submit" ng-disabled="form.$invalid" ng-click="test()" class="btn">Login</button>
|
<button type="submit" ng-click="test()" class="btn">Login</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
@ -12,10 +12,6 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<form class="form" name="form">
|
<form class="form" name="form">
|
||||||
<! TODO use error messages -->
|
|
||||||
<!--<p class="form__error-message" ng-show="errMsg">{{errMsg}}</p>
|
|
||||||
<p class="form__error-message" ng-show="form.$invalid">Please fill out all required fields!</p>-->
|
|
||||||
|
|
||||||
<div class="form__row">
|
<div class="form__row">
|
||||||
<input class="input-text" type="password" ng-model="oldPassphrase" placeholder="Current passphrase" tabindex="1" focus-me="true">
|
<input class="input-text" type="password" ng-model="oldPassphrase" placeholder="Current passphrase" tabindex="1" focus-me="true">
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,6 +35,7 @@ describe('Login (existing user) Controller unit test', function() {
|
|||||||
location = $location;
|
location = $location;
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
scope.state = {};
|
scope.state = {};
|
||||||
|
scope.form = {};
|
||||||
ctrl = $controller(LoginExistingCtrl, {
|
ctrl = $controller(LoginExistingCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {}
|
$routeParams: {}
|
||||||
@ -50,64 +51,37 @@ describe('Login (existing user) Controller unit test', function() {
|
|||||||
|
|
||||||
describe('initial state', function() {
|
describe('initial state', function() {
|
||||||
it('should be well defined', function() {
|
it('should be well defined', function() {
|
||||||
expect(scope.buttonEnabled).to.be.true;
|
expect(scope.incorrect).to.be.undefined;
|
||||||
expect(scope.incorrect).to.be.false;
|
|
||||||
expect(scope.change).to.exist;
|
|
||||||
expect(scope.confirmPassphrase).to.exist;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('functionality', function() {
|
describe('confirm passphrase', function() {
|
||||||
describe('change', function() {
|
it('should unlock crypto and start', function() {
|
||||||
it('should set incorrect to false', function() {
|
var keypair = {},
|
||||||
scope.incorrect = true;
|
pathSpy = sinon.spy(location, 'path');
|
||||||
|
scope.passphrase = passphrase;
|
||||||
|
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, keypair);
|
||||||
|
emailDaoMock.unlock.withArgs({
|
||||||
|
keypair: keypair,
|
||||||
|
passphrase: passphrase
|
||||||
|
}).yields();
|
||||||
|
authMock.storeCredentials.yields();
|
||||||
|
|
||||||
scope.change();
|
scope.confirmPassphrase();
|
||||||
expect(scope.incorrect).to.be.false;
|
|
||||||
});
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
|
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||||
|
expect(pathSpy.calledOnce).to.be.true;
|
||||||
|
expect(pathSpy.calledWith('/desktop')).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('confirm passphrase', function() {
|
it('should not work when keypair unavailable', function() {
|
||||||
it('should unlock crypto and start', function() {
|
scope.passphrase = passphrase;
|
||||||
var keypair = {},
|
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(new Error('asd'));
|
||||||
pathSpy = sinon.spy(location, 'path');
|
|
||||||
scope.passphrase = passphrase;
|
|
||||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(null, keypair);
|
|
||||||
emailDaoMock.unlock.withArgs({
|
|
||||||
keypair: keypair,
|
|
||||||
passphrase: passphrase
|
|
||||||
}).yields();
|
|
||||||
authMock.storeCredentials.yields();
|
|
||||||
|
|
||||||
|
scope.confirmPassphrase();
|
||||||
scope.confirmPassphrase();
|
expect(scope.errMsg).to.exist;
|
||||||
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
|
||||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
|
||||||
expect(pathSpy.calledOnce).to.be.true;
|
|
||||||
expect(pathSpy.calledWith('/desktop')).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not do anything without passphrase', function() {
|
|
||||||
var pathSpy = sinon.spy(location, 'path');
|
|
||||||
scope.passphrase = '';
|
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
|
||||||
expect(pathSpy.callCount).to.equal(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not work when keypair unavailable', function(done) {
|
|
||||||
scope.passphrase = passphrase;
|
|
||||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(new Error('asd'));
|
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err.message).to.equal('asd');
|
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -9,7 +9,7 @@ var Auth = require('../../src/js/bo/auth'),
|
|||||||
|
|
||||||
describe('Login (initial user) Controller unit test', function() {
|
describe('Login (initial user) Controller unit test', function() {
|
||||||
var scope, ctrl, location, origEmailDao, emailDaoMock,
|
var scope, ctrl, location, origEmailDao, emailDaoMock,
|
||||||
origAuth, authMock,
|
origAuth, authMock, newsletterStub,
|
||||||
emailAddress = 'fred@foo.com',
|
emailAddress = 'fred@foo.com',
|
||||||
keyId, expectedKeyId,
|
keyId, expectedKeyId,
|
||||||
cryptoMock;
|
cryptoMock;
|
||||||
@ -31,17 +31,19 @@ describe('Login (initial user) Controller unit test', function() {
|
|||||||
emailAddress: emailAddress,
|
emailAddress: emailAddress,
|
||||||
};
|
};
|
||||||
|
|
||||||
angular.module('logininitialtest', []);
|
angular.module('logininitialtest', ['woServices']);
|
||||||
mocks.module('logininitialtest');
|
mocks.module('logininitialtest');
|
||||||
mocks.inject(function($rootScope, $controller, $location) {
|
mocks.inject(function($rootScope, $controller, $location, newsletter) {
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
location = $location;
|
location = $location;
|
||||||
|
newsletterStub = sinon.stub(newsletter, 'signup');
|
||||||
scope.state = {
|
scope.state = {
|
||||||
ui: {}
|
ui: {}
|
||||||
};
|
};
|
||||||
ctrl = $controller(LoginInitialCtrl, {
|
ctrl = $controller(LoginInitialCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {}
|
$routeParams: {},
|
||||||
|
newsletter: newsletter
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -58,140 +60,65 @@ describe('Login (initial user) Controller unit test', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('signUpToNewsletter', function() {
|
|
||||||
var xhrMock, requests;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
xhrMock = sinon.useFakeXMLHttpRequest();
|
|
||||||
requests = [];
|
|
||||||
|
|
||||||
xhrMock.onCreate = function(xhr) {
|
|
||||||
requests.push(xhr);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function() {
|
|
||||||
xhrMock.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not signup', function() {
|
|
||||||
scope.state.newsletter = false;
|
|
||||||
|
|
||||||
scope.signUpToNewsletter();
|
|
||||||
expect(requests.length).to.equal(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail', function(done) {
|
|
||||||
scope.state.newsletter = true;
|
|
||||||
|
|
||||||
scope.signUpToNewsletter(function(err, xhr) {
|
|
||||||
expect(err).to.exist;
|
|
||||||
expect(xhr).to.not.exist;
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
|
||||||
requests[0].onerror('err');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work without callback', function() {
|
|
||||||
scope.state.newsletter = true;
|
|
||||||
|
|
||||||
scope.signUpToNewsletter();
|
|
||||||
|
|
||||||
expect(requests.length).to.equal(1);
|
|
||||||
requests[0].respond(200, {
|
|
||||||
"Content-Type": "text/plain"
|
|
||||||
}, 'foobar!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('go to import key', function() {
|
describe('go to import key', function() {
|
||||||
var signUpToNewsletterStub;
|
it('should not continue if terms are not accepted', function() {
|
||||||
beforeEach(function() {
|
scope.agree = undefined;
|
||||||
signUpToNewsletterStub = sinon.stub(scope, 'signUpToNewsletter');
|
|
||||||
});
|
|
||||||
afterEach(function() {
|
|
||||||
signUpToNewsletterStub.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not continue if terms are not accepted', function(done) {
|
|
||||||
scope.state.agree = undefined;
|
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err.message).to.contain('Terms');
|
|
||||||
expect(signUpToNewsletterStub.called).to.be.false;
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.importKey();
|
scope.importKey();
|
||||||
|
|
||||||
|
expect(scope.errMsg).to.contain('Terms');
|
||||||
|
expect(newsletterStub.called).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function() {
|
it('should work', function() {
|
||||||
scope.state.agree = true;
|
scope.agree = true;
|
||||||
scope.importKey();
|
scope.importKey();
|
||||||
expect(signUpToNewsletterStub.calledOnce).to.be.true;
|
expect(newsletterStub.calledOnce).to.be.true;
|
||||||
expect(location.$$path).to.equal('/login-new-device');
|
expect(location.$$path).to.equal('/login-new-device');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('generate key', function() {
|
describe('generate key', function() {
|
||||||
var signUpToNewsletterStub;
|
it('should not continue if terms are not accepted', function() {
|
||||||
beforeEach(function() {
|
scope.agree = undefined;
|
||||||
signUpToNewsletterStub = sinon.stub(scope, 'signUpToNewsletter');
|
|
||||||
});
|
|
||||||
afterEach(function() {
|
|
||||||
signUpToNewsletterStub.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not continue if terms are not accepted', function(done) {
|
|
||||||
scope.state.agree = undefined;
|
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err.message).to.contain('Terms');
|
|
||||||
expect(scope.state.ui).to.equal(1);
|
|
||||||
expect(signUpToNewsletterStub.called).to.be.false;
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.generateKey();
|
scope.generateKey();
|
||||||
|
|
||||||
|
expect(scope.errMsg).to.contain('Terms');
|
||||||
|
expect(scope.state.ui).to.equal(1);
|
||||||
|
expect(newsletterStub.called).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail due to error in emailDao.unlock', function(done) {
|
it('should fail due to error in emailDao.unlock', function() {
|
||||||
scope.state.agree = true;
|
scope.agree = true;
|
||||||
|
|
||||||
emailDaoMock.unlock.withArgs({
|
emailDaoMock.unlock.withArgs({
|
||||||
passphrase: undefined
|
passphrase: undefined
|
||||||
}).yields(new Error());
|
}).yields(new Error('asdf'));
|
||||||
authMock.storeCredentials.yields();
|
authMock.storeCredentials.yields();
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err).to.exist;
|
|
||||||
expect(scope.state.ui).to.equal(1);
|
|
||||||
expect(signUpToNewsletterStub.called).to.be.true;
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.generateKey();
|
scope.generateKey();
|
||||||
expect(scope.state.ui).to.equal(2);
|
|
||||||
|
expect(scope.errMsg).to.exist;
|
||||||
|
expect(scope.state.ui).to.equal(1);
|
||||||
|
expect(newsletterStub.called).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unlock crypto', function(done) {
|
it('should unlock crypto', function() {
|
||||||
scope.state.agree = true;
|
scope.agree = true;
|
||||||
|
|
||||||
emailDaoMock.unlock.withArgs({
|
emailDaoMock.unlock.withArgs({
|
||||||
passphrase: undefined
|
passphrase: undefined
|
||||||
}).yields();
|
}).yields();
|
||||||
authMock.storeCredentials.yields();
|
authMock.storeCredentials.yields();
|
||||||
|
|
||||||
scope.$apply = function() {
|
|
||||||
expect(scope.state.ui).to.equal(2);
|
|
||||||
expect(location.$$path).to.equal('/desktop');
|
|
||||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.generateKey();
|
scope.generateKey();
|
||||||
|
|
||||||
|
expect(scope.errMsg).to.not.exist;
|
||||||
|
expect(scope.state.ui).to.equal(2);
|
||||||
|
expect(newsletterStub.called).to.be.true;
|
||||||
|
expect(location.$$path).to.equal('/desktop');
|
||||||
|
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -37,6 +37,7 @@ describe('Login (new device) Controller unit test', function() {
|
|||||||
scope.state = {
|
scope.state = {
|
||||||
ui: {}
|
ui: {}
|
||||||
};
|
};
|
||||||
|
scope.form = {};
|
||||||
ctrl = $controller(LoginNewDeviceCtrl, {
|
ctrl = $controller(LoginNewDeviceCtrl, {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$routeParams: {}
|
$routeParams: {}
|
||||||
@ -103,7 +104,7 @@ describe('Login (new device) Controller unit test', function() {
|
|||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work when keypair upload fails', function(done) {
|
it('should not work when keypair upload fails', function() {
|
||||||
scope.passphrase = passphrase;
|
scope.passphrase = passphrase;
|
||||||
scope.key = {
|
scope.key = {
|
||||||
privateKeyArmored: 'b'
|
privateKeyArmored: 'b'
|
||||||
@ -123,19 +124,15 @@ describe('Login (new device) Controller unit test', function() {
|
|||||||
errMsg: 'yo mamma.'
|
errMsg: 'yo mamma.'
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err.errMsg).to.equal('yo mamma.');
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
scope.confirmPassphrase();
|
||||||
|
|
||||||
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(keychainMock.putUserKeyPair.calledOnce).to.be.true;
|
expect(keychainMock.putUserKeyPair.calledOnce).to.be.true;
|
||||||
|
expect(scope.errMsg).to.equal('yo mamma.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work when unlock fails', function(done) {
|
it('should not work when unlock fails', function() {
|
||||||
scope.passphrase = passphrase;
|
scope.passphrase = passphrase;
|
||||||
scope.key = {
|
scope.key = {
|
||||||
privateKeyArmored: 'b'
|
privateKeyArmored: 'b'
|
||||||
@ -154,33 +151,28 @@ describe('Login (new device) Controller unit test', function() {
|
|||||||
errMsg: 'yo mamma.'
|
errMsg: 'yo mamma.'
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err.errMsg).to.equal('yo mamma.');
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
scope.confirmPassphrase();
|
||||||
|
|
||||||
expect(scope.incorrect).to.be.true;
|
expect(scope.incorrect).to.be.true;
|
||||||
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(scope.errMsg).to.equal('yo mamma.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not work when keypair retrieval', function(done) {
|
it('should not work when keypair retrieval', function() {
|
||||||
scope.passphrase = passphrase;
|
scope.passphrase = passphrase;
|
||||||
|
scope.key = {
|
||||||
|
privateKeyArmored: 'b'
|
||||||
|
};
|
||||||
|
|
||||||
keychainMock.getUserKeyPair.withArgs(emailAddress).yields({
|
keychainMock.getUserKeyPair.withArgs(emailAddress).yields({
|
||||||
errMsg: 'yo mamma.'
|
errMsg: 'yo mamma.'
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err.errMsg).to.equal('yo mamma.');
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
scope.confirmPassphrase();
|
||||||
|
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
|
expect(scope.errMsg).to.equal('yo mamma.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -33,6 +33,8 @@ describe('Login Private Key Download Controller unit test', function() {
|
|||||||
mocks.inject(function($controller, $rootScope) {
|
mocks.inject(function($controller, $rootScope) {
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
scope.state = {};
|
scope.state = {};
|
||||||
|
scope.tokenForm = {};
|
||||||
|
scope.codeForm = {};
|
||||||
ctrl = $controller(LoginPrivateKeyDownloadCtrl, {
|
ctrl = $controller(LoginPrivateKeyDownloadCtrl, {
|
||||||
$location: location,
|
$location: location,
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
@ -55,6 +57,35 @@ describe('Login Private Key Download Controller unit test', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('checkToken', function() {
|
||||||
|
var verifyRecoveryTokenStub;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
verifyRecoveryTokenStub = sinon.stub(scope, 'verifyRecoveryToken');
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
verifyRecoveryTokenStub.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail for empty recovery token', function() {
|
||||||
|
scope.tokenForm.$invalid = true;
|
||||||
|
|
||||||
|
scope.checkToken();
|
||||||
|
|
||||||
|
expect(verifyRecoveryTokenStub.calledOnce).to.be.false;
|
||||||
|
expect(scope.errMsg).to.exist;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work', function() {
|
||||||
|
verifyRecoveryTokenStub.yields();
|
||||||
|
|
||||||
|
scope.checkToken();
|
||||||
|
|
||||||
|
expect(verifyRecoveryTokenStub.calledOnce).to.be.true;
|
||||||
|
expect(scope.step).to.equal(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('verifyRecoveryToken', function() {
|
describe('verifyRecoveryToken', function() {
|
||||||
var testKeypair = {
|
var testKeypair = {
|
||||||
publicKey: {
|
publicKey: {
|
||||||
@ -62,53 +93,35 @@ describe('Login Private Key Download Controller unit test', function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should fail for empty recovery token', function(done) {
|
it('should fail in keychain.getUserKeyPair', function() {
|
||||||
scope.onError = function(err) {
|
keychainMock.getUserKeyPair.yields(new Error('asdf'));
|
||||||
expect(err).to.exist;
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.recoveryToken = undefined;
|
|
||||||
scope.verifyRecoveryToken();
|
scope.verifyRecoveryToken();
|
||||||
|
|
||||||
|
expect(scope.errMsg).to.exist;
|
||||||
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail in keychain.getUserKeyPair', function(done) {
|
it('should fail in keychain.downloadPrivateKey', function() {
|
||||||
keychainMock.getUserKeyPair.yields(42);
|
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err).to.exist;
|
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.recoveryToken = 'token';
|
|
||||||
scope.verifyRecoveryToken();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail in keychain.downloadPrivateKey', function(done) {
|
|
||||||
keychainMock.getUserKeyPair.yields(null, testKeypair);
|
keychainMock.getUserKeyPair.yields(null, testKeypair);
|
||||||
keychainMock.downloadPrivateKey.yields(42);
|
keychainMock.downloadPrivateKey.yields(new Error('asdf'));
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err).to.exist;
|
|
||||||
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
|
||||||
expect(keychainMock.downloadPrivateKey.calledOnce).to.be.true;
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.recoveryToken = 'token';
|
scope.recoveryToken = 'token';
|
||||||
|
|
||||||
scope.verifyRecoveryToken();
|
scope.verifyRecoveryToken();
|
||||||
|
|
||||||
|
expect(scope.errMsg).to.exist;
|
||||||
|
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
|
||||||
|
expect(keychainMock.downloadPrivateKey.calledOnce).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work', function(done) {
|
it('should work', function() {
|
||||||
keychainMock.getUserKeyPair.yields(null, testKeypair);
|
keychainMock.getUserKeyPair.yields(null, testKeypair);
|
||||||
keychainMock.downloadPrivateKey.yields(null, 'encryptedPrivateKey');
|
keychainMock.downloadPrivateKey.yields(null, 'encryptedPrivateKey');
|
||||||
|
|
||||||
scope.recoveryToken = 'token';
|
scope.recoveryToken = 'token';
|
||||||
scope.verifyRecoveryToken(function() {
|
|
||||||
expect(scope.encryptedPrivateKey).to.equal('encryptedPrivateKey');
|
scope.verifyRecoveryToken(function() {});
|
||||||
done();
|
|
||||||
});
|
expect(scope.encryptedPrivateKey).to.equal('encryptedPrivateKey');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -151,39 +164,20 @@ describe('Login Private Key Download Controller unit test', function() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail on empty code', function(done) {
|
it('should fail on decryptAndStorePrivateKeyLocally', function() {
|
||||||
scope.code0 = '';
|
keychainMock.decryptAndStorePrivateKeyLocally.yields(new Error('asdf'));
|
||||||
scope.code1 = '';
|
|
||||||
scope.code2 = '';
|
|
||||||
scope.code3 = '';
|
|
||||||
scope.code4 = '';
|
|
||||||
scope.code5 = '';
|
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err).to.exist;
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.decryptAndStorePrivateKeyLocally();
|
scope.decryptAndStorePrivateKeyLocally();
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail on decryptAndStorePrivateKeyLocally', function(done) {
|
expect(scope.errMsg).to.exist;
|
||||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(42);
|
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
||||||
|
|
||||||
scope.onError = function(err) {
|
|
||||||
expect(err).to.exist;
|
|
||||||
expect(keychainMock.decryptAndStorePrivateKeyLocally.calledOnce).to.be.true;
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.decryptAndStorePrivateKeyLocally();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should goto /login-existing on emailDao.unlock fail', function(done) {
|
it('should goto /login-existing on emailDao.unlock fail', function(done) {
|
||||||
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
|
keychainMock.decryptAndStorePrivateKeyLocally.yields(null, {
|
||||||
encryptedKey: 'keyArmored'
|
encryptedKey: 'keyArmored'
|
||||||
});
|
});
|
||||||
emailDaoMock.unlock.yields(42);
|
emailDaoMock.unlock.yields(new Error('asdf'));
|
||||||
|
|
||||||
scope.goTo = function(location) {
|
scope.goTo = function(location) {
|
||||||
expect(location).to.equal('/login-existing');
|
expect(location).to.equal('/login-existing');
|
||||||
@ -213,30 +207,6 @@ describe('Login Private Key Download Controller unit test', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('goForward', function() {
|
|
||||||
it('should work in step 1', function() {
|
|
||||||
var verifyRecoveryTokenStub = sinon.stub(scope, 'verifyRecoveryToken');
|
|
||||||
verifyRecoveryTokenStub.yields();
|
|
||||||
scope.step = 1;
|
|
||||||
|
|
||||||
scope.goForward();
|
|
||||||
|
|
||||||
expect(verifyRecoveryTokenStub.calledOnce).to.be.true;
|
|
||||||
expect(scope.step).to.equal(2);
|
|
||||||
verifyRecoveryTokenStub.restore();
|
|
||||||
});
|
|
||||||
it('should work in step 2', function() {
|
|
||||||
var decryptAndStorePrivateKeyLocallyStub = sinon.stub(scope, 'decryptAndStorePrivateKeyLocally');
|
|
||||||
decryptAndStorePrivateKeyLocallyStub.returns();
|
|
||||||
scope.step = 2;
|
|
||||||
|
|
||||||
scope.goForward();
|
|
||||||
|
|
||||||
expect(decryptAndStorePrivateKeyLocallyStub.calledOnce).to.be.true;
|
|
||||||
decryptAndStorePrivateKeyLocallyStub.restore();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('goTo', function() {
|
describe('goTo', function() {
|
||||||
it('should work', function(done) {
|
it('should work', function(done) {
|
||||||
mocks.inject(function($controller, $rootScope, $location) {
|
mocks.inject(function($controller, $rootScope, $location) {
|
||||||
|
76
test/unit/newsletter-service-test.js
Normal file
76
test/unit/newsletter-service-test.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var mocks = angular.mock;
|
||||||
|
require('../../src/js/service/newsletter');
|
||||||
|
|
||||||
|
describe('Newsletter Service unit test', function() {
|
||||||
|
var newsletter;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
angular.module('newsletter-test', ['woServices']);
|
||||||
|
mocks.module('newsletter-test');
|
||||||
|
mocks.inject(function($injector) {
|
||||||
|
newsletter = $injector.get('newsletter');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {});
|
||||||
|
|
||||||
|
describe('signup', function() {
|
||||||
|
var xhrMock, requests;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
xhrMock = sinon.useFakeXMLHttpRequest();
|
||||||
|
requests = [];
|
||||||
|
|
||||||
|
xhrMock.onCreate = function(xhr) {
|
||||||
|
requests.push(xhr);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
xhrMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not signup if user has not agreed', inject(function($rootScope) {
|
||||||
|
newsletter.signup('text@example.com', false).then(function(result) {
|
||||||
|
expect(result).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
$rootScope.$apply();
|
||||||
|
expect(requests.length).to.equal(0);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not signup due to invalid email address', inject(function($rootScope) {
|
||||||
|
newsletter.signup('textexample.com', true).catch(function(err) {
|
||||||
|
expect(err.message).to.contain('Invalid');
|
||||||
|
});
|
||||||
|
|
||||||
|
$rootScope.$apply();
|
||||||
|
expect(requests.length).to.equal(0);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fail', inject(function($rootScope) {
|
||||||
|
newsletter.signup('text@example.com', true).catch(function(err) {
|
||||||
|
expect(err).to.exist;
|
||||||
|
});
|
||||||
|
|
||||||
|
requests[0].onerror('err');
|
||||||
|
$rootScope.$apply();
|
||||||
|
expect(requests.length).to.equal(1);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should work', inject(function($rootScope) {
|
||||||
|
newsletter.signup('text@example.com', true).then(function(result) {
|
||||||
|
expect(result).to.exist;
|
||||||
|
});
|
||||||
|
|
||||||
|
requests[0].respond(200, {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
}, 'foobar!');
|
||||||
|
$rootScope.$apply();
|
||||||
|
expect(requests.length).to.equal(1);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user