mirror of
https://github.com/moparisthebest/mail
synced 2024-11-22 17:02:17 -05:00
[WO-462] Rework login workflow
* Make keygen and import possibilities clearer in login-initial * Show spinner when generating key * Use mobile design (wide buttons) everywhere * Show info about key-sync in login-new-device (for mobile users) * remove info popovers in login screens * allow keyfile import even when keysync has been activated
This commit is contained in:
parent
9b618cc20f
commit
e19d8a4e5b
@ -9,7 +9,8 @@ define(function(require) {
|
|||||||
|
|
||||||
states = {
|
states = {
|
||||||
IDLE: 1,
|
IDLE: 1,
|
||||||
PROCESSING: 2,
|
SET_PASSPHRASE: 2,
|
||||||
|
PROCESSING: 3,
|
||||||
DONE: 4
|
DONE: 4
|
||||||
};
|
};
|
||||||
$scope.state.ui = states.IDLE; // initial state
|
$scope.state.ui = states.IDLE; // initial state
|
||||||
@ -29,6 +30,17 @@ define(function(require) {
|
|||||||
$location.path('/login-new-device');
|
$location.path('/login-new-device');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.setPassphrase = function() {
|
||||||
|
if (!$scope.state.agree) {
|
||||||
|
$scope.onError({
|
||||||
|
message: termsMsg
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.setState(states.SET_PASSPHRASE);
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Taken from jQuery validate.password plug-in 1.0
|
* Taken from jQuery validate.password plug-in 1.0
|
||||||
* http://bassistance.de/jquery-plugins/jquery-plugin-validate.password/
|
* http://bassistance.de/jquery-plugins/jquery-plugin-validate.password/
|
||||||
@ -90,20 +102,14 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$scope.state.agree) {
|
|
||||||
$scope.onError({
|
|
||||||
message: termsMsg
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.setState(states.PROCESSING);
|
$scope.setState(states.PROCESSING);
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
emailDao.unlock({
|
emailDao.unlock({
|
||||||
passphrase: (passphrase) ? passphrase : undefined
|
passphrase: (passphrase) ? passphrase : undefined
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
$scope.setState(states.IDLE);
|
$scope.setState(states.SET_PASSPHRASE);
|
||||||
$scope.onError(err);
|
$scope.onError(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include respond-to(desktop) {
|
@include respond-to(desktop) {
|
||||||
margin: 135px auto 75px;
|
margin: 115px auto 75px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.working {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
font-size: 150%;
|
font-size: 150%;
|
||||||
}
|
}
|
||||||
@ -38,10 +42,7 @@
|
|||||||
color: $btn-color;
|
color: $btn-color;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
width: 100%;
|
||||||
@include respond-to(mobile) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p, label {
|
p, label {
|
||||||
@ -52,6 +53,18 @@
|
|||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
margin: 30px 0 40px;
|
||||||
|
|
||||||
|
legend {
|
||||||
|
color: $color-blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
@ -98,17 +111,6 @@
|
|||||||
.popover-info {
|
.popover-info {
|
||||||
display: none; // hide on mobile
|
display: none; // hide on mobile
|
||||||
}
|
}
|
||||||
|
|
||||||
@include respond-to(desktop) {
|
|
||||||
input[type="text"],
|
|
||||||
input[type="password"],
|
|
||||||
input[type="file"] {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
.popover-info {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,19 +124,25 @@
|
|||||||
|
|
||||||
.view-login-privatekey-download {
|
.view-login-privatekey-download {
|
||||||
.content {
|
.content {
|
||||||
max-width: 500px;
|
|
||||||
|
|
||||||
input.code {
|
fieldset {
|
||||||
margin-right: 0;
|
margin-top: 20px;
|
||||||
width: auto;
|
}
|
||||||
|
|
||||||
|
.code {
|
||||||
|
max-width: 240px;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin-right: 0;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-login-set-credentials {
|
.view-login-set-credentials {
|
||||||
.content {
|
.content {
|
||||||
max-width: 450px;
|
|
||||||
|
|
||||||
b, a {
|
b, a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
<form>
|
<form>
|
||||||
<div>
|
<div>
|
||||||
<input class="input-text" type="password" ng-model="passphrase" ng-change="change()" ng-class="{'input-text-error':incorrect}" placeholder="Passphrase" tabindex="1" focus-me="true">
|
<input class="input-text" type="password" ng-model="passphrase" ng-change="change()" ng-class="{'input-text-error':incorrect}" placeholder="Passphrase" tabindex="1" focus-me="true">
|
||||||
<span class="popover-info" data-icon-append="" popover="#passphrase-info"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<a href="https://whiteout.io/revocation.html" title="Click here to reset your account." target="_blank">Forgot your passphrase?</a>
|
<a href="https://whiteout.io/revocation.html" title="Click here to reset your account." target="_blank">Forgot your passphrase?</a>
|
||||||
<div>
|
<div>
|
||||||
@ -17,15 +16,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div><!--/content-->
|
</div><!--/content-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- popovers -->
|
|
||||||
<div id="passphrase-info" class="popover right" ng-controller="PopoverCtrl">
|
|
||||||
<div class="arrow"></div>
|
|
||||||
<div class="popover-title"><b>What is this?</b></div>
|
|
||||||
<div class="popover-content">
|
|
||||||
<p>A passphrase is like a password that protects your PGP key.</p>
|
|
||||||
<p>There is no way to access your messages without your passphrase.</p>
|
|
||||||
<p>If you have forgotten your passphrase, please request an account reset using the provided link. You will not be able to read previous messages after a reset.</p>
|
|
||||||
</div>
|
|
||||||
</div><!--/.popover-->
|
|
@ -1,21 +1,14 @@
|
|||||||
<div class="view-login view-login-initial" ng-class="{'waiting-cursor': state.ui === 2}">
|
<div class="view-login view-login-initial" ng-class="{'waiting-cursor': state.ui === 3}">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img src="img/whiteout_logo.svg" alt="whiteout.io">
|
<img src="img/whiteout_logo.svg" alt="whiteout.io">
|
||||||
</div><!--/logo-->
|
</div><!--/logo-->
|
||||||
|
|
||||||
<div class="content" ng-switch on="state.ui">
|
<div class="content">
|
||||||
|
|
||||||
|
<div ng-show="state.ui === 1">
|
||||||
|
<p><b>PGP key.</b> You can either import an existing PGP key or generate a new one. Your private key remains on your device and is not sent to our servers.</p>
|
||||||
|
|
||||||
<div ng-switch-when="1">
|
|
||||||
<p><b>PGP key.</b> You can either import an existing PGP key or generate a new one.</p>
|
|
||||||
<p>If you want to generate a new key, you can set a passphrase to protect your key on disk.</p>
|
|
||||||
<form>
|
<form>
|
||||||
<div>
|
|
||||||
<label class="input-error-message" ng-class="{'passphrase-label-ok': passphraseRating >= 2}">{{passphraseMsg}}</label><br>
|
|
||||||
<input class="input-text" type="password" ng-model="state.passphrase" ng-change="checkPassphraseQuality()" placeholder="Enter passphrase" tabindex="1" focus-me="true">
|
|
||||||
<input class="input-text" type="password" ng-model="state.confirmation" ng-class="{'input-text-error': (state.confirmation || state.passphrase) && state.confirmation !== state.passphrase}" placeholder="Confirm passphrase" tabindex="2">
|
|
||||||
<span class="popover-info" data-icon-append="" popover="#passphrase-info"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<input type="checkbox" ng-model="state.agree" name="checkbox" id="checkbox_id">
|
<input type="checkbox" ng-model="state.agree" name="checkbox" id="checkbox_id">
|
||||||
<label for="checkbox_id">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>.</label>
|
<label for="checkbox_id">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>.</label>
|
||||||
@ -23,24 +16,33 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button wo-touch="importKey()" class="btn btn-alt">Import existing key</button>
|
<button wo-touch="importKey()" class="btn btn-alt">Import existing key</button>
|
||||||
<button type="submit" wo-touch="confirmPassphrase()" class="btn" ng-disabled="(state.confirmation || state.passphrase) && state.confirmation !== state.passphrase" tabindex="3">Generate new key</button>
|
<button type="submit" wo-touch="setPassphrase()" class="btn"tabindex="3">Generate new key</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-switch-when="2">
|
<div ng-show="state.ui === 2">
|
||||||
|
<p><b>Set passphrase.</b> You can set a passphrase to protect your key on disk. This must be entered everytime you start the app. For no passphrase just press continue.</p>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<div>
|
||||||
|
<label class="input-error-message" ng-class="{'passphrase-label-ok': passphraseRating >= 2}">{{passphraseMsg}}</label><br>
|
||||||
|
<input class="input-text" type="password" ng-model="state.passphrase" ng-change="checkPassphraseQuality()" placeholder="Enter passphrase" tabindex="1" focus-me="true">
|
||||||
|
<input class="input-text" type="password" ng-model="state.confirmation" ng-class="{'input-text-error': (state.confirmation || state.passphrase) && state.confirmation !== state.passphrase}" placeholder="Confirm passphrase" tabindex="2">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button type="submit" wo-touch="confirmPassphrase()" class="btn" ng-disabled="(state.confirmation || state.passphrase) && state.confirmation !== state.passphrase" tabindex="3">Continue</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="state.ui === 3">
|
||||||
<p><b>Generating key.</b> Please stand by. This can take a while...</p>
|
<p><b>Generating key.</b> Please stand by. This can take a while...</p>
|
||||||
|
<div class="working">
|
||||||
|
<span class="spinner"></span>
|
||||||
|
</div><!--/.working-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div><!--/content-->
|
</div><!--/content-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- popovers -->
|
|
||||||
<div id="passphrase-info" class="popover right" ng-controller="PopoverCtrl">
|
|
||||||
<div class="arrow"></div>
|
|
||||||
<div class="popover-title"><b>What is this?</b></div>
|
|
||||||
<div class="popover-content">
|
|
||||||
<p>A passphrase is like a password that protects your PGP key.</p>
|
|
||||||
<p>If your device is lost or stolen the passphrase protects the contents of your mailbox.</p>
|
|
||||||
</div>
|
|
||||||
</div><!--/.popover-->
|
|
@ -4,40 +4,22 @@
|
|||||||
</div><!--/logo-->
|
</div><!--/logo-->
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p><b>Import keyfile.</b> To access your emails on this device, please import your existing key file.</p>
|
<p><b>Import PGP key.</b> Please import an existing key from the file system.</p>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>On a mobile device?</legend>
|
||||||
|
<p>If you cannot import your key via a USB stick, you can setup <i>Key sync</i> on a desktop PC to securely transfer your PGP key over the Whiteout cloud. <a href="https://blog.whiteout.io/2014/07/07/secure-pgp-key-sync-a-proposal/" target="_blank">Learn more</a>.</p>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<form>
|
<form>
|
||||||
<div>
|
<div>
|
||||||
<input type="file" accept=".asc" file-reader tabindex="1">
|
<input type="file" accept=".asc" file-reader tabindex="1">
|
||||||
<span class="popover-info" data-icon-append="" popover="#keyfile-info"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input class="input-text" type="password" ng-model="passphrase" ng-class="{'input-text-error':incorrect}" placeholder="Passphrase" tabindex="2" focus-me="true">
|
<input class="input-text" type="password" ng-model="passphrase" ng-class="{'input-text-error':incorrect}" placeholder="Passphrase" tabindex="2" focus-me="true">
|
||||||
<span class="popover-info" data-icon-append="" popover="#passphrase-info"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<a href="https://whiteout.io/revocation.html" title="Click here to reset your account." target="_blank">Lost your keyfile or passphrase?</a>
|
<a href="https://whiteout.io/revocation.html" title="Click here to reset your account." target="_blank">Lost your keyfile or passphrase?</a>
|
||||||
<div><button type="submit" wo-touch="confirmPassphrase()" class="btn" ng-disabled="!key" tabindex="3">Import</button>
|
<div><button type="submit" wo-touch="confirmPassphrase()" class="btn" ng-disabled="!key" tabindex="3">Import</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- popovers -->
|
|
||||||
<div id="keyfile-info" class="popover right" ng-controller="PopoverCtrl">
|
|
||||||
<div class="arrow"></div>
|
|
||||||
<div class="popover-title"><b>What is this?</b></div>
|
|
||||||
<div class="popover-content">
|
|
||||||
<p>The keyfile contains your PGP keys.</p>
|
|
||||||
<p>It can be exported on your first computer under "Account".</p>
|
|
||||||
<p>You can import it from a USB flash drive. Never send the keyfile to yourself via email.</p>
|
|
||||||
</div>
|
|
||||||
</div><!--/.popover-->
|
|
||||||
|
|
||||||
<div id="passphrase-info" class="popover right" ng-controller="PopoverCtrl">
|
|
||||||
<div class="arrow"></div>
|
|
||||||
<div class="popover-title"><b>What is this?</b></div>
|
|
||||||
<div class="popover-content">
|
|
||||||
<p>A passphrase is like a password that protects your PGP key.</p>
|
|
||||||
<p>There is no way to access your messages without your passphrase.</p>
|
|
||||||
<p>If you have forgotten your passphrase, please request an account reset using the provided link. You will not be able to read previous messages after a reset.</p>
|
|
||||||
</div>
|
|
||||||
</div><!--/.popover-->
|
|
@ -7,10 +7,14 @@
|
|||||||
|
|
||||||
<div class="step" ng-show="step === 1">
|
<div class="step" ng-show="step === 1">
|
||||||
<p><b>Key sync.</b> We have sent you an email containing a recovery token. Please copy and paste the token below to download your key.</p>
|
<p><b>Key sync.</b> We have sent you an email containing a recovery token. Please copy and paste the token below to download your key.</p>
|
||||||
<p>You can also just import the key file manually e.g. if you're on a device with USB access.</p>
|
|
||||||
|
|
||||||
<input type="text" class="input-text" size="42" ng-model="recoveryToken" placeholder="Recovery token" focus-me="step === 1">
|
<input type="text" class="input-text" size="42" ng-model="recoveryToken" placeholder="Recovery token" focus-me="step === 1">
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Got USB?</legend>
|
||||||
|
<p>You can also import the key file manually if you're on a device with USB access and your key is on a flash drive.</p>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<a class="btn btn-alt" href="#login-new-device">Import key file</a>
|
<a class="btn btn-alt" href="#login-new-device">Import key file</a>
|
||||||
<button class="btn" wo-touch="goForward()">Confirm token</button>
|
<button class="btn" wo-touch="goForward()">Confirm token</button>
|
||||||
@ -20,12 +24,14 @@
|
|||||||
<div class="step" ng-show="step === 2">
|
<div class="step" ng-show="step === 2">
|
||||||
<p><b>Key sync.</b> Please enter the keychain code you wrote down during sync setup.</p>
|
<p><b>Key sync.</b> Please enter the keychain code you wrote down during sync setup.</p>
|
||||||
|
|
||||||
<input type="text" class="input-text code" size="4" maxlength="4" ng-model="code0" focus-me="step === 2" focus-next ng-paste="handlePaste($event)"> -
|
<div class="code">
|
||||||
<input type="text" class="input-text code" size="4" maxlength="4" ng-model="code1" focus-next> -
|
<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 code" size="4" maxlength="4" ng-model="code2" focus-next> -
|
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code1" focus-next> -
|
||||||
<input type="text" class="input-text code" size="4" maxlength="4" ng-model="code3" focus-next> -
|
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code2" focus-next> -
|
||||||
<input type="text" class="input-text code" size="4" maxlength="4" ng-model="code4" focus-next> -
|
<input type="text" class="input-text" size="4" maxlength="4" ng-model="code3" focus-next> -
|
||||||
<input type="text" class="input-text code" size="4" maxlength="4" ng-model="code5">
|
<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="code5">
|
||||||
|
</div>
|
||||||
<!--<a href="https://whiteout.io/revocation.html" title="Click here to reset your account." target="_blank">Lost your keychain code?</a>-->
|
<!--<a href="https://whiteout.io/revocation.html" title="Click here to reset your account." target="_blank">Lost your keychain code?</a>-->
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</div><!--/.settings-->
|
</div><!--/.settings-->
|
||||||
</div><!--/.details-->
|
</div><!--/.details-->
|
||||||
|
|
||||||
<div ng-show="busy">
|
<div class="working" ng-show="busy">
|
||||||
<span class="spinner"></span>
|
<span class="spinner"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -123,12 +123,8 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('confirm passphrase', function() {
|
describe('setPassphrase', function() {
|
||||||
var setStateStub;
|
|
||||||
|
|
||||||
it('should not continue if terms are not accepted', function(done) {
|
it('should not continue if terms are not accepted', function(done) {
|
||||||
scope.state.passphrase = passphrase;
|
|
||||||
scope.state.confirmation = passphrase;
|
|
||||||
scope.state.agree = undefined;
|
scope.state.agree = undefined;
|
||||||
|
|
||||||
scope.onError = function(err) {
|
scope.onError = function(err) {
|
||||||
@ -136,13 +132,28 @@ define(function(require) {
|
|||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.confirmPassphrase();
|
scope.setPassphrase();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should continue', function(done) {
|
||||||
|
scope.state.agree = true;
|
||||||
|
|
||||||
|
var setStateStub = sinon.stub(scope, 'setState', function(state) {
|
||||||
|
expect(setStateStub.calledOnce).to.be.true;
|
||||||
|
expect(state).to.equal(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.setPassphrase();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('confirm passphrase', function() {
|
||||||
|
var setStateStub;
|
||||||
|
|
||||||
it('should unlock crypto', function(done) {
|
it('should unlock crypto', function(done) {
|
||||||
scope.state.passphrase = passphrase;
|
scope.state.passphrase = passphrase;
|
||||||
scope.state.confirmation = passphrase;
|
scope.state.confirmation = passphrase;
|
||||||
scope.state.agree = true;
|
|
||||||
|
|
||||||
emailDaoMock.unlock.withArgs({
|
emailDaoMock.unlock.withArgs({
|
||||||
passphrase: passphrase
|
passphrase: passphrase
|
||||||
@ -168,7 +179,6 @@ define(function(require) {
|
|||||||
it('should not work when keypair generation fails', function(done) {
|
it('should not work when keypair generation fails', function(done) {
|
||||||
scope.state.passphrase = passphrase;
|
scope.state.passphrase = passphrase;
|
||||||
scope.state.confirmation = passphrase;
|
scope.state.confirmation = passphrase;
|
||||||
scope.state.agree = true;
|
|
||||||
|
|
||||||
emailDaoMock.unlock.withArgs({
|
emailDaoMock.unlock.withArgs({
|
||||||
passphrase: passphrase
|
passphrase: passphrase
|
||||||
@ -176,9 +186,9 @@ define(function(require) {
|
|||||||
|
|
||||||
setStateStub = sinon.stub(scope, 'setState', function(state) {
|
setStateStub = sinon.stub(scope, 'setState', function(state) {
|
||||||
if (setStateStub.calledOnce) {
|
if (setStateStub.calledOnce) {
|
||||||
expect(state).to.equal(2);
|
expect(state).to.equal(3);
|
||||||
} else if (setStateStub.calledTwice) {
|
} else if (setStateStub.calledTwice) {
|
||||||
expect(state).to.equal(1);
|
expect(state).to.equal(2);
|
||||||
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
expect(emailDaoMock.unlock.calledOnce).to.be.true;
|
||||||
scope.setState.restore();
|
scope.setState.restore();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user