diff --git a/resources/static/dialog/controllers/authenticate_controller.js b/resources/static/dialog/controllers/authenticate_controller.js index 7233670142d9dd6c95d2c4dc02de87b2683832be..380fa48ed9e5224b005d2b30a71ccec2bcfba81c 100644 --- a/resources/static/dialog/controllers/authenticate_controller.js +++ b/resources/static/dialog/controllers/authenticate_controller.js @@ -91,23 +91,18 @@ function authenticate(el, event) { var email = getEmail(), - pass = dom.getInner("#password"), + pass = helpers.getAndValidatePassword("#password"), self = this; cancelEvent(event); - if (!validation.emailAndPassword(email, pass)) return; - - user.authenticate(email, pass, - function onComplete(authenticated) { - if (authenticated) { - self.close("authenticated", { - email: email - }); - } else { - bid.Tooltip.showTooltip("#cannot_authenticate"); - } - }, self.getErrorDialog(errors.authenticate)); + if(email && pass) { + helpers.authenticateUser.call(self, email, pass, function() { + self.close("authenticated", { + email: email + }); + }); + } } function resetPassword(el, event) { diff --git a/resources/static/dialog/controllers/dialog_controller.js b/resources/static/dialog/controllers/dialog_controller.js index c69e8a8e172ffbc1adc4105611b7eb4270fe57fc..d84c20a488d83b6db9a47e3da32b10f984da4dd7 100644 --- a/resources/static/dialog/controllers/dialog_controller.js +++ b/resources/static/dialog/controllers/dialog_controller.js @@ -61,7 +61,7 @@ options = options || {}; - if(options.window) { + if (options.window) { win = options.window; } @@ -104,9 +104,10 @@ params = {}; } - self.allowPersistent = (params.persistent == true); + self.allowPersistent = !!params.persistent; + self.requiredEmail = params.requiredEmail; - if('onLine' in navigator && !navigator.onLine) { + if ('onLine' in navigator && !navigator.onLine) { self.doOffline(); return; } @@ -241,6 +242,10 @@ this.element.authenticate(info); }, + doAuthenticateWithRequiredEmail: function(info) { + this.element.requiredemail(info); + }, + doForgotPassword: function(email) { this.element.forgotpassword({ email: email @@ -290,13 +295,18 @@ var self=this; user.checkAuthenticationAndSync(function onSuccess() {}, function onComplete(authenticated) { - if (authenticated) { + if (self.requiredEmail) { + self.doAuthenticateWithRequiredEmail({ + email: self.requiredEmail, + authenticated: authenticated + }); + } + else if (authenticated) { self.doPickEmail(); } else { self.doAuthenticate(); } - }, - self.getErrorDialog(errors.checkAuthentication)); + }, self.getErrorDialog(errors.checkAuthentication)); } }); diff --git a/resources/static/dialog/controllers/page_controller.js b/resources/static/dialog/controllers/page_controller.js index ed4a4fdaaf4e5f4866863574c417aece5f62012e..8634c4a6721be594a4ae0ce0f859505e042cb369 100644 --- a/resources/static/dialog/controllers/page_controller.js +++ b/resources/static/dialog/controllers/page_controller.js @@ -48,39 +48,40 @@ init: function(el, options) { options = options || {}; - var me=this, - bodyTemplate = options.bodyTemplate, - bodyVars = options.bodyVars, - errorTemplate = options.errorTemplate, - errorVars = options.errorVars, - waitTemplate = options.waitTemplate, - waitVars = options.waitVars; - + var self=this; - if(bodyTemplate) { - me.renderDialog(bodyTemplate, bodyVars); + if(options.bodyTemplate) { + self.renderDialog(options.bodyTemplate, options.bodyVars); } - if(waitTemplate) { - me.renderWait(waitTemplate, waitVars); + if(options.waitTemplate) { + self.renderWait(options.waitTemplate, options.waitVars); } - if(errorTemplate) { - me.renderError(errorTemplate, errorVars); + if(options.errorTemplate) { + self.renderError(options.errorTemplate, options.errorVars); } + self.start(options); + }, + + start: function() { + var self=this; // XXX move all of these, bleck. - dom.bindEvent("form", "submit", me.onSubmit.bind(me)); - dom.bindEvent("#thisIsNotMe", "click", me.close.bind(me, "notme")); + dom.bindEvent("form", "submit", self.onSubmit.bind(self)); + dom.bindEvent("#thisIsNotMe", "click", self.close.bind(self, "notme")); }, - destroy: function() { + stop: function() { dom.unbindEvent("form", "submit"); dom.unbindEvent("input", "keyup"); dom.unbindEvent("#thisIsNotMe", "click"); dom.removeClass("body", "waiting"); + }, + destroy: function() { + this.stop(); this._super(); }, diff --git a/resources/static/dialog/controllers/pickemail_controller.js b/resources/static/dialog/controllers/pickemail_controller.js index 3a79c113ce0c4f9d1283c5e68db9a3288ac30046..c11253376646e6191085c949567e6df05dc044b5 100644 --- a/resources/static/dialog/controllers/pickemail_controller.js +++ b/resources/static/dialog/controllers/pickemail_controller.js @@ -45,7 +45,6 @@ helpers = bid.Helpers, dom = bid.DOM, body = $("body"), - animationComplete = body.innerWidth() < 640, assertion; function animateSwap(fadeOutSelector, fadeInSelector, callback) { @@ -97,41 +96,6 @@ return !!identity; } - function tryClose() { - if (typeof assertion !== "undefined" && animationComplete) { - this.close("assertion_generated", { - assertion: assertion - }); - } - } - - function getAssertion(email) { - // Kick of the assertion fetching/keypair generating while we are showing - // the animation, hopefully this minimizes the delay the user notices. - var self=this; - user.getAssertion(email, function(assert) { - assertion = assert || null; - startAnimation.call(self); - }, self.getErrorDialog(errors.getAssertion)); - } - - function startAnimation() { - var self=this; - if (!animationComplete) { - $("#signIn").animate({"width" : "685px"}, "slow", function () { - // post animation - body.delay(500).animate({ "opacity" : "0.5"}, "fast", function () { - animationComplete = true; - tryClose.call(self); - }); - }); - } - else { - tryClose.call(self); - } - - } - function signIn(element, event) { cancelEvent(event); var self=this, @@ -146,7 +110,7 @@ storage.site.set(origin, "remember", $("#remember").is(":checked")); } - getAssertion.call(self, email); + helpers.getAssertion.call(self, email); } } diff --git a/resources/static/dialog/controllers/required_email_controller.js b/resources/static/dialog/controllers/required_email_controller.js new file mode 100644 index 0000000000000000000000000000000000000000..3cb3dc26db70fed0dd58f608fb780bce7c724361 --- /dev/null +++ b/resources/static/dialog/controllers/required_email_controller.js @@ -0,0 +1,142 @@ +/*jshint brgwser:true, jQuery: true, forin: true, laxbreak:true */ +/*global _: true, BrowserID: true, PageController: true */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla BrowserID. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +(function() { + "use strict"; + + var ANIMATION_TIME = 250, + bid = BrowserID, + user = bid.User, + errors = bid.Errors, + helpers = bid.Helpers, + dom = bid.DOM, + assertion; + + function signIn(event) { + event && event.preventDefault(); + + var self = this, + email = self.email; + + // If the user is already authenticated and they own this address, sign + // them right in. + if(self.authenticated) { + helpers.getAssertion.call(self, email); + } + else { + // If the user is not already authenticated, but they potentially own + // this address, try and sign them in and generate an assertion if they + // get the password right. + var password = helpers.getAndValidatePassword("#password"); + if (password) { + helpers.authenticateUser.call(self, email, password, function() { + // Now that the user has authenticated, sync their emails and get an + // assertion for the email we care about. + user.syncEmailKeypair(email, function() { + helpers.getAssertion.call(self, email); + }, self.getErrorDialog(errors.syncEmailKeypair)); + }); + } + } + } + + function verifyAddress(event) { + event.preventDefault(); + + } + + function forgotPassword(event) { + event.preventDefault(); + } + + + PageController.extend("Requiredemail", {}, { + start: function(options) { + var self=this, + email = options.email || "", + authenticated = options.authenticated || false; + + self.email = email; + self.authenticated = authenticated; + + // NOTE: When the app first starts and the user's authentication is + // checked, all email addresses for authenticated users are synced. We + // can be assured by this point that our addresses are up to date. + if(authenticated) { + // if the current user owns the required email, sign in with it + // (without a password). Otherwise, make the user verify the address + // (which shows no password). + var userOwnsEmail = !!user.getStoredEmailKeypair(email); + showTemplate(userOwnsEmail, false); + } + else { + user.isEmailRegistered(email, function(registered) { + // If the current email address is registered but the user is not + // authenticated, make them sign in with it. Otherwise, make them + // verify ownership of the address. + showTemplate(registered, registered); + }, self.getErrorDialog(errors.isEmailRegistered)); + } + + function showTemplate(requireSignin, showPassword) { + self.renderDialog("requiredemail", { + email: email, + signin: requireSignin, + showPassword: showPassword + }); + + dom.bindEvent("#sign_in", "click", signIn.bind(self)); + dom.bindEvent("#verify_address", "click", verifyAddress.bind(self)); + dom.bindEvent("#forgotPassword", "click", forgotPassword.bind(self)); + } + + self._super(); + }, + + stop: function() { + dom.unbindEvent("#sign_in", "click"); + dom.unbindEvent("#verify_address", "click"); + dom.unbindEvent("#forgotPassword", "click"); + + this._super(); + }, + + signIn: signIn, + verifyAddress: verifyAddress, + forgotPassword: forgotPassword + }); + +}()); diff --git a/resources/static/dialog/dialog.js b/resources/static/dialog/dialog.js index 6dd10f88ae3d5ee098d620a31554016abc603dc1..46beb215dc08ac90235dbb0472657e0411158200 100644 --- a/resources/static/dialog/dialog.js +++ b/resources/static/dialog/dialog.js @@ -61,19 +61,22 @@ steal '../shared/screens', '../shared/tooltip', '../shared/validation', - '../shared/helpers', - '../shared/browser-support', - '../shared/browserid-extensions', '../shared/network', '../shared/user', '../shared/error-messages', - '../shared/wait-messages') + '../shared/browser-support', + '../shared/browserid-extensions', + '../shared/wait-messages', + '../shared/helpers' + ) .controllers('page', 'dialog', 'authenticate', 'checkregistration', - 'pickemail') // loads files in controllers folder + 'pickemail', + 'required_email' + ) // loads files in controllers folder .then(function() { $(function() { diff --git a/resources/static/dialog/views/requiredemail.ejs b/resources/static/dialog/views/requiredemail.ejs new file mode 100644 index 0000000000000000000000000000000000000000..f1741c602bc81b23c8565d19503600c5a3031133 --- /dev/null +++ b/resources/static/dialog/views/requiredemail.ejs @@ -0,0 +1,41 @@ + <!--strong>Required Email Sign In</strong--> + <div class="form_section"> + <ul class="inputs"> + + The site requested you sign in with + <p> + <span id="required_email"><%= email %></span> + </p> + + <% if (showPassword) { %> + <li id="password_section"> + + <label for="password" class="half serif">Password</label> + <div class="half right"> + <a id="forgotPassword" href="#" tabindex="4">forgot your password?</a> + </div> + <input id="password" class="sans" type="password" maxlength="80" tabindex="2" /> + + + <div id="password_required" class="tooltip" for="password"> + The password field is required. + </div> + + <div id="cannot_authenticate" class="tooltip" for="password"> + The account cannot be logged in with this username and password. + </div> + </li> + <% } %> + + </ul> + + <div class="submit cf"> + <% if (signin) { %> + <button id="sign_in" tabindex="3">sign in</button> + <% } else { %> + <button id="verify_address" tabindex="3">verify email</button> + <% } %> + + <button id="cancel_stage" tabindex="4">cancel</button> + </div> + </div> diff --git a/resources/static/shared/error-messages.js b/resources/static/shared/error-messages.js index 0b760950f62b26b666ac3f85e30a0059b514131a..38c321996f8aec42f1a911f57e0110bc88453907 100644 --- a/resources/static/shared/error-messages.js +++ b/resources/static/shared/error-messages.js @@ -111,6 +111,10 @@ BrowserID.Errors = (function(){ title: "Syncing Email Addresses" }, + syncEmailKeypair: { + title: "Sync Keys for Address" + }, + xhrError: { title: "Communication Error" } diff --git a/resources/static/shared/helpers.js b/resources/static/shared/helpers.js index fa4d5bd0fa0afd17c0c2a2cbfc1e01f1b461462c..4deabe88ceb5cfadb03c8ede9cad1c49a31fb121 100644 --- a/resources/static/shared/helpers.js +++ b/resources/static/shared/helpers.js @@ -38,6 +38,8 @@ var bid = BrowserID, dom = bid.DOM, + user = bid.User, + errors = bid.Errors, validation = bid.Validation, helpers = bid.Helpers = bid.Helpers || {}; @@ -63,6 +65,53 @@ return password; } + /** + * XXX add a test for the next two and move them into a dialog specific + * helper module! + */ + function animateClose(callback) { + var body = $("body"), + doAnimation = $("#signIn").length && body.innerWidth() > 640; + + if (doAnimation) { + $("#signIn").animate({"width" : "685px"}, "slow", function () { + // post animation + body.delay(500).animate({ "opacity" : "0.5"}, "fast", function () { + callback(); + }); + }); + } + else { + callback(); + } + } + + // XXX Move this to a dialog specific module + function getAssertion(email) { + var self=this; + user.getAssertion(email, function(assert) { + assert = assert || null; + animateClose(function() { + self.close("assertion_generated", { + assertion: assert + }); + }); + }, self.getErrorDialog(errors.getAssertion)); + } + + // XXX Move this to a dialog specific module + function authenticateUser(email, pass, callback) { + var self=this; + user.authenticate(email, pass, + function onComplete(authenticated) { + if (authenticated) { + callback(); + } else { + bid.Tooltip.showTooltip("#cannot_authenticate"); + } + }, self.getErrorDialog(errors.authenticate)); + } + extend(helpers, { /** * Extend an object with the properties of another object. Overwrites @@ -88,6 +137,9 @@ * @return {string} password if password is valid, null otw. */ getAndValidatePassword: getAndValidatePassword, + + getAssertion: getAssertion, + authenticateUser: authenticateUser }); diff --git a/resources/static/test/qunit/controllers/dialog_controller_unit_test.js b/resources/static/test/qunit/controllers/dialog_controller_unit_test.js index fa602bbc490831ace4460a91c039473173ebe83a..a0cf4452dfa394ede27f3f88a4a66a8e7bf79e9b 100644 --- a/resources/static/test/qunit/controllers/dialog_controller_unit_test.js +++ b/resources/static/test/qunit/controllers/dialog_controller_unit_test.js @@ -50,20 +50,21 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/con channelError = false; } - function initController() { - controller = el.dialog({ + function initController(config) { + var config = $.extend(config, { window: { setupChannel: function() { if (channelError) throw "Channel error"; } } - }).controller(); + }); + + controller = el.dialog(config).controller(); } module("controllers/dialog_controller", { setup: function() { reset(); - initController(); }, teardown: function() { @@ -73,22 +74,21 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/con }); test("initialization with channel error", function() { - controller.destroy(); - reset(); channelError = true; - initController(); ok($("#error .contents").text().length, "contents have been written"); }); test("doOffline", function() { + initController(); controller.doOffline(); ok($("#error .contents").text().length, "contents have been written"); ok($("#error #offline").text().length, "offline error message has been written"); }); test("doXHRError while online, no network info given", function() { + initController(); controller.doXHRError(); ok($("#error .contents").text().length, "contents have been written"); ok($("#error #action").text().length, "action contents have been written"); @@ -96,6 +96,7 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/con }); test("doXHRError while online, network info given", function() { + initController(); controller.doXHRError({ network: { type: "POST", @@ -108,6 +109,7 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/con }); test("doXHRError while offline does not update contents", function() { + initController(); controller.doOffline(); $("#error #action").remove(); @@ -116,5 +118,39 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/con }); + /* + test("doCheckAuth with registered requiredEmail, authenticated", function() { + initController({ + requiredEmail: "registered@testuser.com" + }); + + controller.doCheckAuth(); + }); + + test("doCheckAuth with registered requiredEmail, not authenticated", function() { + initController({ + requiredEmail: "registered@testuser.com" + }); + + controller.doCheckAuth(); + }); + + test("doCheckAuth with unregistered requiredEmail, not authenticated", function() { + initController({ + requiredEmail: "unregistered@testuser.com" + }); + + controller.doCheckAuth(); + }); + + test("doCheckAuth with unregistered requiredEmail, authenticated as other user", function() { + initController({ + requiredEmail: "unregistered@testuser.com" + }); + + controller.doCheckAuth(); + }); +*/ + }); diff --git a/resources/static/test/qunit/controllers/pickemail_controller_unit_test.js b/resources/static/test/qunit/controllers/pickemail_controller_unit_test.js index 1a88f607c5e1a41c29abca1b99414aebbc11e44a..d1b416313b86d11c4f3b9e9206925666882b673c 100644 --- a/resources/static/test/qunit/controllers/pickemail_controller_unit_test.js +++ b/resources/static/test/qunit/controllers/pickemail_controller_unit_test.js @@ -78,7 +78,7 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/con var radioButton = $("input[type=radio]").eq(1); ok(radioButton.is(":checked"), "the email address we specified is checked"); - var label = radioButton.parent();; + var label = radioButton.parent(); ok(label.hasClass("preselected"), "the label has the preselected class"); }); @@ -171,5 +171,17 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/con equal(storage.site.get(testOrigin, "remember"), false, "remember saved correctly"); }); + test("addEmail with valid email", function() { + + }); + + test("addEmail with valid email with leading/trailing whitespace", function() { + + }); + + test("addEmail with invalid email", function() { + + }); + }); diff --git a/resources/static/test/qunit/controllers/required_email_controller_unit_test.js b/resources/static/test/qunit/controllers/required_email_controller_unit_test.js new file mode 100644 index 0000000000000000000000000000000000000000..67319c9a7596a66e4e9a2b4dc2810e199e4fd2cd --- /dev/null +++ b/resources/static/test/qunit/controllers/required_email_controller_unit_test.js @@ -0,0 +1,282 @@ +/*jshint browsers:true, forin: true, laxbreak: true */ +/*global steal: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID:true */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla BrowserID. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +steal.then(function() { + "use strict"; + + var el, + controller, + bid = BrowserID, + xhr = bid.Mocks.xhr, + user = bid.User, + network = bid.Network, + storage = bid.Storage, + hub = OpenAjax.hub, + listeners = []; + + // XXX TODO Share this code with the other tests. + function subscribe(message, cb) { + listeners.push(hub.subscribe(message, cb)); + } + + function unsubscribeAll() { + var registration; + while(registration = listeners.pop()) { + hub.unsubscribe(registration); + } + } + + module("controllers/required_email", { + setup: function() { + el = $("body"); + $("#error").hide(); + network.setXHR(xhr); + storage.clear(); + xhr.useResult("valid"); + xhr.setContextInfo({ + authenticated: false + }); + }, + + teardown: function() { + if (controller) { + try { + controller.destroy(); + } catch(e) { + // controller may have already been deleted. + } + controller = null; + } + network.setXHR($); + unsubscribeAll(); + } + }); + + function testSignIn(email, cb) { + setTimeout(function() { + equal($("#required_email").text(), email, "email set correctly"); + equal($("#sign_in").length, 1, "sign in button shown"); + equal($("#verify_address").length, 0, "verify address not shows"); + cb && cb(); + start(); + }, 100); + stop(); + } + + function testVerify(email, cb) { + setTimeout(function() { + equal($("#required_email").text(), email, "email set correctly"); + equal($("#sign_in").length, 0, "sign in button not shown"); + equal($("#verify_address").length, 1, "verify address shows"); + testNoPasswordSection(); + cb && cb(); + start(); + }, 100); + stop(); + } + + function testPasswordSection() { + equal($("#password_section").length, 1, "password section is there"); + } + + function testNoPasswordSection() { + equal($("#password_section").length, 0, "password section is not there"); + } + + test("user who is not authenticated, email is registered", function() { + var email = "registered@testuser.com"; + controller = el.requiredemail({ + email: email, + authenticated: false + }).controller(); + + testSignIn(email, testPasswordSection); + }); + + test("user who is not authenticated, email not registered", function() { + var email = "unregistered@testuser.com"; + controller = el.requiredemail({ + email: email, + authenticated: false + }).controller(); + + testVerify(email); + }); + + test("user who is not authenticated, XHR error", function() { + xhr.useResult("ajaxError"); + var email = "registered@testuser.com"; + controller = el.requiredemail({ + email: email, + authenticated: false + }).controller(); + + stop(); + + setTimeout(function() { + ok($("#error").is(":visible"), "Error message is visible"); + start(); + }, 100); + }); + + test("user who is authenticated, email belongs to user", function() { + xhr.setContextInfo({ + authenticated: true + }); + + var email = "registered@testuser.com"; + user.syncEmailKeypair(email, function() { + controller = el.requiredemail({ + email: email, + authenticated: true + }).controller(); + }); + + testSignIn(email, testNoPasswordSection); + }); + + test("user who is authenticated, email does not belong to user", function() { + xhr.setContextInfo({ + authenticated: true + }); + + var email = "registered@testuser.com"; + user.removeEmail(email, function() { + controller = el.requiredemail({ + email: email, + authenticated: true + }).controller(); + }); + + testVerify(email); + }); + + test("user who is authenticated, but email unknown", function() { + xhr.setContextInfo({ + authenticated: true + }); + + var email = "unregistered@testuser.com"; + controller = el.requiredemail({ + email: email, + authenticated: true + }).controller(); + + testVerify(email); + }); + + + test("signIn of an authenticated user generates an assertion", function() { + xhr.setContextInfo({ + authenticated: true + }); + + var email = "registered@testuser.com"; + user.syncEmailKeypair(email, function() { + controller = el.requiredemail({ + email: email, + authenticated: true + }).controller(); + + subscribe("assertion_generated", function(item, info) { + ok(info.assertion, "we have an assertion"); + start(); + }); + + controller.signIn(); + }); + + stop(); + }); + + test("signIn of an non-authenticated user with a good password generates an assertion", function() { + xhr.setContextInfo({ + authenticated: false + }); + + var email = "registered@testuser.com"; + controller = el.requiredemail({ + email: email, + authenticated: false + }).controller(); + + subscribe("assertion_generated", function(item, info) { + ok(info.assertion, "we have an assertion"); + start(); + }); + + $("#password").val("password"); + controller.signIn(); + + stop(); + }); + + + test("signIn of an non-authenticated user with a bad password does not generate an assertion", function() { + xhr.setContextInfo({ + authenticated: false + }); + + var email = "registered@testuser.com"; + controller = el.requiredemail({ + email: email, + authenticated: false + }).controller(); + + var assertion; + + subscribe("assertion_generated", function(item, info) { + ok(false, "this should not have been called"); + assertion = info.assertion; + }); + + xhr.useResult("invalid"); + $("#password").val("badpassword"); + controller.signIn(); + + setTimeout(function() { + // Since we are using the mock, we know the XHR result is going to be + // back in less than 1000ms. All we have to do is check whether an + // assertion was generated, if so, bad jiji. + equal(typeof assertion, "undefined", "assertion was never generated"); + start(); + }, 1000); + + stop(); + }); + +}); + diff --git a/resources/static/test/qunit/qunit.js b/resources/static/test/qunit/qunit.js index 8a250f9133ea61ad7256dcce2e4a1f865b3964c6..f414ec3074096f102e3f891f7d246d75fd563a17 100644 --- a/resources/static/test/qunit/qunit.js +++ b/resources/static/test/qunit/qunit.js @@ -22,6 +22,9 @@ steal.plugins( "/shared/validation", "/shared/helpers", + "/dialog/controllers/page_controller", + "/dialog/controllers/required_email_controller", + "pages/browserid_unit_test", "pages/page_helpers_unit_test", @@ -44,9 +47,13 @@ steal.plugins( "shared/network_unit_test", "shared/user_unit_test", "resources/channel_unit_test", + "controllers/page_controller_unit_test", "controllers/pickemail_controller_unit_test", "controllers/dialog_controller_unit_test", "controllers/checkregistration_controller_unit_test", - "controllers/authenticate_controller_unit_test"); + "controllers/authenticate_controller_unit_test", + "controllers/required_email_controller_unit_test" + + );