From d30148babc0593367c7ef74f5155636595c70b58 Mon Sep 17 00:00:00 2001 From: Shane Tomlinson <stomlinson@mozilla.com> Date: Tue, 20 Dec 2011 10:21:06 +0000 Subject: [PATCH] End to end primary flow on the signup page. * Adding unit tests for network.addressInfo * to the XHR mock, adding info needed to test primary/secondary flow. * Adding a BrowserID.Mock.Provisioning to handle mocking out provisioning capabilities. * Creating a new User.createUser that handles creating users on either primaryies or secondaries. * Adding unit tests for the signup page for primary, updating old tests for secondary. * Starting a shell of error messages and success markup on signup page. * Starting to hook up the dialog, adding loads of tests. Secondary based user creation good again. --- .../static/dialog/controllers/authenticate.js | 1 - resources/static/dialog/resources/helpers.js | 51 ++- .../static/dialog/resources/state_machine.js | 1 - .../static/dialog/views/authenticate.ejs | 4 + resources/static/pages/signup.js | 66 ++-- resources/static/shared/error-messages.js | 5 + resources/static/shared/network.js | 6 +- resources/static/shared/user.js | 79 +++- resources/static/test/index.html | 11 +- .../controllers/authenticate_unit_test.js | 13 +- resources/static/test/qunit/mocks/mocks.js | 1 - .../static/test/qunit/mocks/provisioning.js | 54 +++ resources/static/test/qunit/mocks/xhr.js | 12 +- .../test/qunit/pages/signup_unit_test.js | 95 +++-- .../test/qunit/resources/helpers_unit_test.js | 70 +++- .../test/qunit/shared/network_unit_test.js | 341 ++++++++++-------- .../test/qunit/shared/user_unit_test.js | 80 +++- .../static/test/qunit/testHelpers/helpers.js | 42 ++- resources/views/signup.ejs | 9 + 19 files changed, 676 insertions(+), 265 deletions(-) create mode 100644 resources/static/test/qunit/mocks/provisioning.js diff --git a/resources/static/dialog/controllers/authenticate.js b/resources/static/dialog/controllers/authenticate.js index 3149d5aab..9d09e264f 100644 --- a/resources/static/dialog/controllers/authenticate.js +++ b/resources/static/dialog/controllers/authenticate.js @@ -128,7 +128,6 @@ BrowserID.Modules.Authenticate = (function() { } function createUserState() { - var self=this; self.publish("create_user"); diff --git a/resources/static/dialog/resources/helpers.js b/resources/static/dialog/resources/helpers.js index 5c5d51667..697146bb8 100644 --- a/resources/static/dialog/resources/helpers.js +++ b/resources/static/dialog/resources/helpers.js @@ -91,17 +91,50 @@ } function createUser(email, callback) { + function complete(status) { + callback && callback(status); + } + var self=this; - user.createUser(email, function(staged) { - if (staged) { - self.close("user_staged", { - email: email - }); - } - else { - tooltip.showTooltip("#could_not_add"); + user.createUser(email, function(status) { + switch(status) { + case "secondary.already_added": + // XXX how to handle this - createUser should not be called on + // already existing addresses, so this path should not be called. + tooltip.showTooltip("#already_registered"); + complete(false); + break; + case "secondary.verify": + self.close("user_staged", { + email: email + }); + complete(true); + break; + case "secondary.could_not_add": + tooltip.showTooltip("#could_not_add"); + complete(false); + break; + case "primary.already_added": + // XXX Is this status possible? + break; + case "primary.verified": + self.close("primay_user_verified", { + email: email + }); + complete(true); + break; + case "primary.verify": + self.close("primay_user_verified", { + email: email + }); + complete(true); + break; + case "primary.could_not_add": + // XXX Can this happen? + break; + default: + break; } - if (callback) callback(staged); }, self.getErrorDialog(errors.createUser, callback)); } diff --git a/resources/static/dialog/resources/state_machine.js b/resources/static/dialog/resources/state_machine.js index beb9f72d2..e611a76ca 100644 --- a/resources/static/dialog/resources/state_machine.js +++ b/resources/static/dialog/resources/state_machine.js @@ -118,7 +118,6 @@ var authenticated = info.authenticated; if (self.requiredEmail) { - // XXX get this out of here and into the state machine! gotoState("doAuthenticateWithRequiredEmail", { email: self.requiredEmail, authenticated: authenticated diff --git a/resources/static/dialog/views/authenticate.ejs b/resources/static/dialog/views/authenticate.ejs index 8ac297c96..376d2e6a1 100644 --- a/resources/static/dialog/views/authenticate.ejs +++ b/resources/static/dialog/views/authenticate.ejs @@ -17,6 +17,10 @@ <div id="could_not_add" class="tooltip" for="email"> We just sent an email to that address! If you really want to send another, wait a minute or two and try again. </div> + + <div id="already_registered" class="tooltip" for="email"> + That email address is already registered! You should try again. + </div> </li> <li id="hint_section" class="start"> diff --git a/resources/static/pages/signup.js b/resources/static/pages/signup.js index fda1de0e6..a70e72fc2 100644 --- a/resources/static/pages/signup.js +++ b/resources/static/pages/signup.js @@ -55,45 +55,45 @@ BrowserID.signUp = (function() { function submit(oncomplete) { var email = helpers.getAndValidateEmail("#email"); - function complete() { - oncomplete && oncomplete(); + function complete(status) { + oncomplete && oncomplete(status); } if (email) { - user.addressInfo(email, function(info) { - if (info.type === 'secondary') { - if (!info.known) { - user.createUser(email, function onSuccess(success) { - if(success) { - pageHelpers.showEmailSent(oncomplete); - } - else { - tooltip.showTooltip("#could_not_add"); - complete(); - } - }, pageHelpers.getFailure(errors.createUser, oncomplete)); - } - else { + user.createUser(email, function onComplete(status) { + switch(status) { + case "secondary.already_added": $('#registeredEmail').html(email); showNotice(".alreadyRegistered"); - complete(); - } - } else { - BrowserID.Provisioning({ - email: email, - url: info.prov - }, function(r) { - // XXX: implement me - alert("shane! provisioning was a success " + JSON.stringify(r)); - }, function(e) { - // XXX: implement me - alert("shane! provisioning was a failure: " + JSON.stringify(e)); - }); + complete(false); + break; + case "secondary.verify": + pageHelpers.showEmailSent(complete); + break; + case "secondary.could_not_add": + tooltip.showTooltip("#could_not_add"); + complete(false); + break; + case "primary.already_added": + // XXX Is this status possible? + break; + case "primary.verified": + pageHelpers.replaceInputsWithNotice("#congrats", complete.bind(null, true)); + break; + case "primary.verify": + // XXX What do we do here? + complete(false); + break; + case "primary.could_not_add": + // XXX Can this happen? + break; + default: + break; } - }, pageHelpers.getFailure(errors.isEmailRegistered, oncomplete)); + }, pageHelpers.getFailure(errors.createUser, oncomplete)); } else { - complete(); + complete(false); } } @@ -105,7 +105,9 @@ BrowserID.signUp = (function() { if (event.which !== 13) $(".notification").fadeOut(ANIMATION_SPEED); } - function init() { + function init(config) { + config = config || {}; + $("form input[autofocus]").focus(); pageHelpers.setupEmail(); diff --git a/resources/static/shared/error-messages.js b/resources/static/shared/error-messages.js index 340af7f66..dc444e193 100644 --- a/resources/static/shared/error-messages.js +++ b/resources/static/shared/error-messages.js @@ -82,6 +82,11 @@ BrowserID.Errors = (function(){ message: "Unfortunately, BrowserID cannot communicate while offline!" }, + provisioningPrimary: { + title: "Provisioning Primary", + message: "We had trouble communicating with your email provider, please try again!" + }, + registration: { title: "Registration Failed" }, diff --git a/resources/static/shared/network.js b/resources/static/shared/network.js index cbad5de4f..16c0f60b8 100644 --- a/resources/static/shared/network.js +++ b/resources/static/shared/network.js @@ -481,7 +481,7 @@ BrowserID.Network = (function() { * (is it a primary or a secondary) * @method addressInfo * @param {string} email - Email address to check. - * @param {function} [onSuccess] - Called with an object on success, + * @param {function} [onComplete] - Called with an object on success, * containing these properties: * type: <secondary|primary> * known: boolean, present - present if type is secondary @@ -489,11 +489,11 @@ BrowserID.Network = (function() { * prov: string - url to embed for silent provisioning - present if type is secondary * @param {function} [onFailure] - Called on XHR failure. */ - addressInfo: function(email, onSuccess, onFailure) { + addressInfo: function(email, onComplete, onFailure) { get({ url: "/wsapi/address_info?email=" + encodeURIComponent(email), success: function(data, textStatus, xhr) { - if (onSuccess) onSuccess(data); + if (onComplete) onComplete(data); }, error: onFailure }); diff --git a/resources/static/shared/user.js b/resources/static/shared/user.js index 522269041..58509bb8c 100644 --- a/resources/static/shared/user.js +++ b/resources/static/shared/user.js @@ -39,9 +39,11 @@ BrowserID.User = (function() { "use strict"; var jwk, jwt, vep, jwcert, origin, - network = BrowserID.Network, - storage = BrowserID.Storage, - User, pollTimeout; + bid = BrowserID, + network = bid.Network, + storage = bid.Storage, + User, pollTimeout, + provisioning = bid.Provisioning; function prepareDeps() { if (!jwk) { @@ -52,7 +54,6 @@ BrowserID.User = (function() { } } - "use strict"; // remove identities that are no longer valid function cleanupIdentities(cb) { network.serverTime(function(serverTime) { @@ -220,6 +221,16 @@ BrowserID.User = (function() { } User = { + init: function(config) { + if(config.provisioning) { + provisioning = config.provisioning; + } + }, + + reset: function() { + provisioning = BrowserID.Provisioning; + }, + /** * Set the interface to use for networking. Used for unit testing. * @method setNetwork @@ -259,18 +270,70 @@ BrowserID.User = (function() { /** * Create a user account - this creates an user account that must be verified. - * @method createUser + * @method createSecondaryUser * @param {string} email - Email address. - * @param {function} [onSuccess] - Called on successful completion. + * @param {function} [onComplete] - Called on completion. * @param {function} [onFailure] - Called on error. */ - createUser: function(email, onSuccess, onFailure) { + createSecondaryUser: function(email, onComplete, onFailure) { var self=this; // remember this for later storage.setStagedOnBehalfOf(self.getHostname()); - network.createUser(email, origin, onSuccess, onFailure); + network.createUser(email, origin, onComplete, onFailure); + }, + + /** + * Status: + * "already_added", "verify_secondary", "secondary_could_not_add", "verify_primary", + * "primary_verified" + */ + createUser: function(email, onComplete, onFailure) { + var self=this; + + function attemptAddSecondary(email, info) { + if (info.known) { + onComplete("secondary.already_added"); + } + else { + self.createSecondaryUser(email, function(success) { + if(success) { + onComplete("secondary.verify"); + } + else { + onComplete("secondary.could_not_add"); + } + }, onFailure); + } + } + + function attemptAddPrimary(email, info) { + // XXX Can we know if the primary is already known to us? + provisioning({ + email: email, + url: info.prov + }, function(r) { + onComplete("primary.verified"); + }, function(info) { + // XXX When do we have to redirect off to the authentication page? + // Would a code like this come in on the failure mode? + if(info.code === "MUST_AUTHENTICATE") { + onComplete("primary.verify"); + } + else { + onFailure(info); + } + }); + } + + network.addressInfo(email, function(info) { + if (info.type === 'secondary') { + attemptAddSecondary(email, info); + } else { + attemptAddPrimary(email, info); + } + }, onFailure); }, /** diff --git a/resources/static/test/index.html b/resources/static/test/index.html index a6fb34070..b3400342e 100644 --- a/resources/static/test/index.html +++ b/resources/static/test/index.html @@ -44,10 +44,6 @@ <input id="new_password" /> <input type="checkbox" id="remember" /> </div> - <div id="congrats">Congrats!</div> - <span id="cannotconfirm" class="error">Cannot confirm</span> - <span id="cannotcommunicate" class="error">Cannot communicate</span> - <span class="siteinfo" class="error"><span class="website"></span></span> <span class="hint">Hint</span> </div> @@ -63,8 +59,12 @@ <ul class="notifications"> + <li id="cannotconfirm" class="error notification">Cannot confirm</li> + <li id="cannotcommunicate" class="error notification">Cannot communicate</li> + <li class="siteinfo" class="error notification"><span class="website"></span></li> <li class="notification emailsent">Email Sent</li> <li class="notification doh">doh</li> + <li class="notification" id="congrats">Congratulations!</li> </ul> <ul id="emailList"> @@ -89,6 +89,7 @@ <script type="text/javascript" src="qunit/mocks/mocks.js"></script> <script type="text/javascript" src="qunit/mocks/xhr.js"></script> <script type="text/javascript" src="qunit/mocks/templates.js"></script> + <script type="text/javascript" src="qunit/mocks/provisioning.js"></script> <script type="text/javascript" src="/shared/javascript-extensions.js"></script> <script type="text/javascript" src="/shared/renderer.js"></script> <script type="text/javascript" src="/shared/class.js"></script> @@ -166,7 +167,7 @@ <script type="text/javascript" src="qunit/controllers/authenticate_unit_test.js"></script> <script type="text/javascript" src="qunit/controllers/forgotpassword_unit_test.js"></script> <script type="text/javascript" src="qunit/controllers/required_email_unit_test.js"></script> - // must go last or all other tests will fail. + <!-- must go last or all other tests will fail. --> <script type="text/javascript" src="qunit/controllers/dialog_unit_test.js"></script> </body> </html> diff --git a/resources/static/test/qunit/controllers/authenticate_unit_test.js b/resources/static/test/qunit/controllers/authenticate_unit_test.js index 7a0d73ed1..60763b5fa 100644 --- a/resources/static/test/qunit/controllers/authenticate_unit_test.js +++ b/resources/static/test/qunit/controllers/authenticate_unit_test.js @@ -47,7 +47,9 @@ userCreated = true, mediator = bid.Mediator, registrations = [], - register = bid.TestHelpers.register; + testHelpers = bid.TestHelpers, + register = testHelpers.register, + provisioning = bid.Mocks.Provisioning; function reset() { emailRegistered = false; @@ -63,7 +65,7 @@ module("controllers/authenticate", { setup: function() { reset(); - bid.TestHelpers.setup(); + testHelpers.setup(); createController(); }, @@ -76,7 +78,7 @@ } } reset(); - bid.TestHelpers.teardown(); + testHelpers.teardown(); } }); @@ -153,6 +155,9 @@ asyncTest("createUser with valid email", function() { $("#email").val("unregistered@testuser.com"); + + xhr.useResult("unknown_secondary"); + register("user_staged", function(msg, info) { equal(info.email, "unregistered@testuser.com", "user_staged with correct email triggered"); start(); @@ -189,8 +194,6 @@ equal(bid.Tooltip.shown, true, "tooltip is shown"); start(); }); - - }); asyncTest("createUser with valid email, XHR error", function() { diff --git a/resources/static/test/qunit/mocks/mocks.js b/resources/static/test/qunit/mocks/mocks.js index 70a00e1ff..68d2223b3 100644 --- a/resources/static/test/qunit/mocks/mocks.js +++ b/resources/static/test/qunit/mocks/mocks.js @@ -1,4 +1,3 @@ - /*jshint browsers:true, forin: true, laxbreak: true */ /*global BrowserID: true */ /* ***** BEGIN LICENSE BLOCK ***** diff --git a/resources/static/test/qunit/mocks/provisioning.js b/resources/static/test/qunit/mocks/provisioning.js new file mode 100644 index 000000000..70a8760e6 --- /dev/null +++ b/resources/static/test/qunit/mocks/provisioning.js @@ -0,0 +1,54 @@ +/*jshint browsers:true, forin: true, laxbreak: true */ +/*global 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 ***** */ +BrowserID.Mocks.Provisioning = (function() { + function Provisioning(info, onsuccess, onfailure) { + if(Provisioning.failure) onfailure(Provisioning.failure); + else onsuccess(); + } + + Provisioning.setSuccess = function(status) { + Provisioning.status = status; + }; + + Provisioning.setFailure = function(status) { + Provisioning.failure = status; + } + + return Provisioning; +}()); + + diff --git a/resources/static/test/qunit/mocks/xhr.js b/resources/static/test/qunit/mocks/xhr.js index 8593376f0..e2f48b980 100644 --- a/resources/static/test/qunit/mocks/xhr.js +++ b/resources/static/test/qunit/mocks/xhr.js @@ -70,6 +70,7 @@ BrowserID.Mocks.xhr = (function() { "post /wsapi/complete_email_addition valid": { success: true }, "post /wsapi/complete_email_addition invalid": { success: false }, "post /wsapi/complete_email_addition ajaxError": undefined, + "post /wsapi/stage_user unknown_secondary": { success: true }, "post /wsapi/stage_user valid": { success: true }, "post /wsapi/stage_user invalid": { success: false }, "post /wsapi/stage_user throttle": 403, @@ -112,7 +113,16 @@ BrowserID.Mocks.xhr = (function() { "get /wsapi/list_emails complete": {"registered@testuser.com":{}}, "post /wsapi/update_password valid": { success: true }, "post /wsapi/update_password incorrectPassword": { success: false }, - "post /wsapi/update_password invalid": undefined + "post /wsapi/update_password invalid": undefined, + "get /wsapi/address_info?email=unregistered%40testuser.com invalid": undefined, + "get /wsapi/address_info?email=unregistered%40testuser.com throttle": { type: "secondary", known: false }, + "get /wsapi/address_info?email=unregistered%40testuser.com unknown_secondary": { type: "secondary", known: false }, + "get /wsapi/address_info?email=registered%40testuser.com known_secondary": { type: "secondary", known: true }, + "get /wsapi/address_info?email=unregistered%40testuser.com primary": { type: "primary", auth: "", prov: "" }, + "get /wsapi/address_info?email=testuser%40testuser.com unknown_secondary": { type: "secondary", known: false }, + "get /wsapi/address_info?email=testuser%40testuser.com known_secondary": { type: "secondary", known: true }, + "get /wsapi/address_info?email=testuser%40testuser.com primary": { type: "primary", auth: "", prov: "" }, + "get /wsapi/address_info?email=testuser%40testuser.com ajaxError": undefined }, setContextInfo: function(field, value) { diff --git a/resources/static/test/qunit/pages/signup_unit_test.js b/resources/static/test/qunit/pages/signup_unit_test.js index 24bf65256..b55022f0a 100644 --- a/resources/static/test/qunit/pages/signup_unit_test.js +++ b/resources/static/test/qunit/pages/signup_unit_test.js @@ -39,39 +39,35 @@ var bid = BrowserID, network = bid.Network, - user = bid.User, xhr = bid.Mocks.xhr, - testOrigin = "http://browserid.org"; + testOrigin = "http://browserid.org", + testHelpers = bid.TestHelpers, + provisioning = bid.Mocks.Provisioning; module("pages/signup", { setup: function() { - network.setXHR(xhr); - $(".error").removeClass("error"); - $("#error").stop().hide(); - $(".notification").stop().hide(); - xhr.useResult("valid"); - user.setOrigin(testOrigin); + testHelpers.setup(); bid.signUp(); }, teardown: function() { - network.setXHR($); - $(".error").removeClass("error"); - $("#error").stop().hide(); - $(".notification").stop().hide(); - $("#error .message").remove(); + testHelpers.teardown(); bid.signUp.reset(); } }); - function testNoticeNotVisible(extraTests) { - bid.signUp.submit(function() { + function testNotRegistered(extraTests) { + bid.signUp.submit(function(status) { + strictEqual(status, false, "address was not registered"); equal($(".emailsent").is(":visible"), false, "email not sent, notice not visible"); + if(extraTests) extraTests(); start(); }); } - asyncTest("signup with valid unregistered email", function() { + asyncTest("signup with valid unregistered secondary email", function() { + xhr.useResult("unknown_secondary"); + $("#email").val("unregistered@testuser.com"); bid.signUp.submit(function() { @@ -81,6 +77,8 @@ }); asyncTest("signup with valid unregistered email with leading/trailing whitespace", function() { + xhr.useResult("unknown_secondary"); + $("#email").val(" unregistered@testuser.com "); bid.signUp.submit(function() { @@ -90,35 +88,37 @@ }); asyncTest("signup with valid registered email", function() { + xhr.useResult("known_secondary"); $("#email").val("registered@testuser.com"); - testNoticeNotVisible(); + testNotRegistered(); }); asyncTest("signup with invalid email address", function() { $("#email").val("invalid"); - testNoticeNotVisible(); + testNotRegistered(); }); asyncTest("signup with throttling", function() { xhr.useResult("throttle"); - $("#email").val("throttled@testuser.com"); + $("#email").val("unregistered@testuser.com"); - testNoticeNotVisible(); + testNotRegistered(); }); - asyncTest("signup with invalid XHR error", function() { + asyncTest("signup with XHR error", function() { xhr.useResult("invalid"); $("#email").val("unregistered@testuser.com"); - testNoticeNotVisible(function() { - equal($("#error").is(":visible"), true, "error message displayed"); + testNotRegistered(function() { + testHelpers.testErrorVisible(); }); }); - asyncTest("signup with unregistered email and cancel button pressed", function() { + asyncTest("signup with unregistered secondary email and cancel button pressed", function() { + xhr.useResult("unknown_secondary"); $("#email").val("unregistered@testuser.com"); bid.signUp.submit(function() { @@ -131,4 +131,51 @@ }); }); + asyncTest("signup with primary email address, provisioning failure - expect error screen", function() { + xhr.useResult("primary"); + + $("#email").val("unregistered@testuser.com"); + provisioning.setFailure({ + code: "internal", + msg: "doowap" + }); + + bid.signUp.submit(function(status) { + equal(status, false, "provisioning failure, status false"); + testHelpers.testErrorVisible(); + start(); + }); + }); + + asyncTest("signup with primary email address, user verified by primary - print success message", function() { + xhr.useResult("primary"); + + $("#email").val("unregistered@testuser.com"); + + provisioning.setSuccess(true); + + bid.signUp.submit(function(status) { + equal(status, true, "primary addition success - true status"); + equal($(".notification:visible").length, 1, "success notification is visible"); + start(); + }); + }); + + // XXX what do we expect here? + asyncTest("signup with primary email address, user must verify with primary - ", function() { + xhr.useResult("primary"); + + $("#email").val("unregistered@testuser.com"); + + provisioning.setFailure({ + code: "MUST_AUTHENTICATE", + msg: "Wahhooo!!" + }); + + bid.signUp.submit(function(status) { + equal(status, false, "user must authenticate, some action needed."); + start(); + }); + }); + }()); diff --git a/resources/static/test/qunit/resources/helpers_unit_test.js b/resources/static/test/qunit/resources/helpers_unit_test.js index ed31c29ba..8fd762395 100644 --- a/resources/static/test/qunit/resources/helpers_unit_test.js +++ b/resources/static/test/qunit/resources/helpers_unit_test.js @@ -44,8 +44,12 @@ storage = bid.Storage, tooltip = bid.Tooltip, testHelpers = bid.TestHelpers, + user = bid.User, + provisioning = bid.Mocks.Provisioning, closeCB, - errorCB; + errorCB, + expectedError = testHelpers.expectXHRFailure, + badError = testHelpers.unexpectedXHRFailure; var controllerMock = { close: function(message, info) { @@ -72,14 +76,6 @@ } } - function badError() { - ok(false, "error should have never been called"); - } - - function expectedError() { - ok(true, "error condition expected"); - start(); - } function badClose() { ok(false, "close should have never been called"); @@ -90,10 +86,14 @@ testHelpers.setup(); closeCB = errorCB = null; errorCB = badError; + user.init({ + provisioning: provisioning + }); }, teardown: function() { testHelpers.teardown(); + user.reset(); } }); @@ -113,10 +113,7 @@ xhr.useResult("ajaxError"); storage.addEmail("registered@testuser.com", {}); - dialogHelpers.getAssertion.call(controllerMock, "registered@testuser.com", function() { - ok(false, "unexpected finish"); - start(); - }); + dialogHelpers.getAssertion.call(controllerMock, "registered@testuser.com", testHelpers.unexpectedSuccess); }); asyncTest("authenticateUser happy case", function() { @@ -144,7 +141,18 @@ }); }); - asyncTest("createUser happy case", function() { + asyncTest("createUser with known secondary, user not staged", function() { + closeCB = badClose; + + xhr.useResult("known_secondary"); + dialogHelpers.createUser.call(controllerMock, "registered@testuser.com", function(staged) { + equal(staged, false, "user was not staged"); + start(); + }); + }); + + asyncTest("createUser with unknown secondary happy case, expect 'user_staged' message", function() { + xhr.useResult("unknown_secondary"); closeCB = expectedClose("user_staged", "email", "unregistered@testuser.com"); dialogHelpers.createUser.call(controllerMock, "unregistered@testuser.com", function(staged) { @@ -153,23 +161,45 @@ }); }); - asyncTest("createUser could not create case", function() { + asyncTest("createUser with unknown secondary, user throttled", function() { closeCB = badClose; - xhr.useResult("invalid"); - dialogHelpers.createUser.call(controllerMock, "registered@testuser.com", function(staged) { + xhr.useResult("throttle"); + dialogHelpers.createUser.call(controllerMock, "unregistered@testuser.com", function(staged) { equal(staged, false, "user was not staged"); start(); }); }); - asyncTest("createUser with XHR error", function() { errorCB = expectedError; xhr.useResult("ajaxError"); - dialogHelpers.createUser.call(controllerMock, "registered@testuser.com", function(staged) { - ok(false, "complete should not have been called"); + dialogHelpers.createUser.call(controllerMock, "registered@testuser.com", testHelpers.unexpectedSuccess); + }); + + asyncTest("createUser with unknown primary, user verified - expect 'primary_user_verified' message", function() { + closeCB = expectedClose("primary_user_verified", "email", "unregistered@testuser.com"); + + xhr.useResult("primary"); + provisioning.setSuccess(true); + + dialogHelpers.createUser.call(controllerMock, "unregistered@testuser.com", function(staged) { + equal(staged, true, "user was staged"); + start(); + }); + }); + + asyncTest("createUser with unknown primary, user must verify with IdP - expect 'primary_verify_user' message", function() { + closeCB = expectedClose("primary_verify_user", "email", "unregistered@testuser.com"); + + xhr.useResult("primary"); + provisioning.setFailure({ + code: "MUST_AUTHENTICATE" + }); + + dialogHelpers.createUser.call(controllerMock, "unregistered@testuser.com", function(staged) { + equal(staged, true, "user was staged"); start(); }); }); diff --git a/resources/static/test/qunit/shared/network_unit_test.js b/resources/static/test/qunit/shared/network_unit_test.js index 00c342dc2..dc7d44725 100644 --- a/resources/static/test/qunit/shared/network_unit_test.js +++ b/resources/static/test/qunit/shared/network_unit_test.js @@ -1,5 +1,5 @@ /*jshint browsers:true, forin: true, laxbreak: true */ -/*global wrappedAsyncTest: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID: true */ +/*global asyncTest: 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 * @@ -37,22 +37,10 @@ (function() { "use strict"; - var testName, - bid = BrowserID, + var bid = BrowserID, mediator = bid.Mediator, - xhr = bid.Mocks.xhr; - - function wrappedAsyncTest(name, test) { - asyncTest(name, function() { - testName = name; - test(); - }); - } - - function wrappedStart() { - console.log("start: " + testName); - start(); - } + xhr = bid.Mocks.xhr, + testHelpers = bid.TestHelpers; function notificationCheck(cb) { // Take the original arguments, take off the function. Add any additional @@ -70,7 +58,7 @@ ok(info.network.type, "request type is in network info"); equal(info.network.textStatus, "errorStatus", "textStatus is in network info"); equal(info.network.errorThrown, "errorThrown", "errorThrown is in response info"); - wrappedStart(); + start(); mediator.unsubscribe(handle); }; @@ -81,22 +69,29 @@ } } + function unexpectedFailure() { + return function() { + ok(false, "unexpected failure"); + start(); + } + } + function failureCheck(cb) { // Take the original arguments, take off the function. Add any additional // arguments that were passed in, and then tack on the onSuccess and // onFailure to the end. Then call the callback. var args = Array.prototype.slice.call(arguments, 1); - args.push(function onSuccess(authenticated) { + args.push(function onSuccess() { ok(false, "XHR failure should never pass"); - wrappedStart(); + start(); }, function onFailure(info) { ok(true, "XHR failure should never pass"); ok(info.network.url, "url is in network info"); ok(info.network.type, "request type is in network info"); equal(info.network.textStatus, "errorStatus", "textStatus is in network info"); equal(info.network.errorThrown, "errorThrown", "errorThrown is in response info"); - wrappedStart(); + start(); }); xhr.useResult("ajaxError"); @@ -108,72 +103,71 @@ module("shared/network", { setup: function() { - network.setXHR(xhr); - xhr.useResult("valid"); + testHelpers.setup(); }, teardown: function() { - network.setXHR($); + testHelpers.teardown(); } }); - wrappedAsyncTest("authenticate with valid user", function() { + asyncTest("authenticate with valid user", function() { network.authenticate("testuser@testuser.com", "testuser", function onSuccess(authenticated) { equal(authenticated, true, "valid authentication"); - wrappedStart(); + start(); }, function onFailure() { ok(false, "valid authentication"); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("authenticate with invalid user", function() { + asyncTest("authenticate with invalid user", function() { xhr.useResult("invalid"); network.authenticate("testuser@testuser.com", "invalid", function onSuccess(authenticated) { equal(authenticated, false, "invalid authentication"); - wrappedStart(); + start(); }, function onFailure() { ok(false, "invalid authentication"); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("authenticate with XHR failure, checking whether application is notified", function() { + asyncTest("authenticate with XHR failure, checking whether application is notified", function() { notificationCheck(network.authenticate, "testuser@testuser.com", "ajaxError"); }); - wrappedAsyncTest("authenticate with XHR failure after context already setup", function() { + asyncTest("authenticate with XHR failure after context already setup", function() { failureCheck(network.authenticate, "testuser@testuser.com", "ajaxError"); }); - wrappedAsyncTest("checkAuth with valid authentication", function() { + asyncTest("checkAuth with valid authentication", function() { xhr.setContextInfo("authenticated", true); network.checkAuth(function onSuccess(authenticated) { equal(authenticated, true, "we have an authentication"); - wrappedStart(); + start(); }, function onFailure() { ok(false, "checkAuth failure"); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("checkAuth with invalid authentication", function() { + asyncTest("checkAuth with invalid authentication", function() { xhr.useResult("invalid"); xhr.setContextInfo("authenticated", false); network.checkAuth(function onSuccess(authenticated) { equal(authenticated, false, "we are not authenticated"); - wrappedStart(); + start(); }, function onFailure() { ok(false, "checkAuth failure"); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("checkAuth with XHR failure", function() { + asyncTest("checkAuth with XHR failure", function() { xhr.useResult("ajaxError"); xhr.setContextInfo("authenticated", false); @@ -182,371 +176,399 @@ // request, we do not test whether the app is notified of an XHR failure network.checkAuth(function onSuccess() { ok(true, "checkAuth does not make an ajax call, all good"); - wrappedStart(); + start(); }, function onFailure() { ok(false, "checkAuth does not make an ajax call, should not fail"); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("logout", function() { + asyncTest("logout", function() { network.logout(function onSuccess() { ok(true, "we can logout"); - wrappedStart(); + start(); }, function onFailure() { ok(false, "logout failure"); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("logout with XHR failure", function() { + asyncTest("logout with XHR failure", function() { notificationCheck(network.logout); }); - wrappedAsyncTest("logout with XHR failure", function() { + asyncTest("logout with XHR failure", function() { failureCheck(network.logout); }); - wrappedAsyncTest("complete_email_addition valid", function() { + asyncTest("complete_email_addition valid", function() { network.completeEmailRegistration("goodtoken", function onSuccess(proven) { equal(proven, true, "good token proved"); - wrappedStart(); + start(); }, function onFailure() { - wrappedStart(); + start(); }); }); - wrappedAsyncTest("complete_email_addition with invalid token", function() { + asyncTest("complete_email_addition with invalid token", function() { xhr.useResult("invalid"); network.completeEmailRegistration("badtoken", function onSuccess(proven) { equal(proven, false, "bad token could not be proved"); - wrappedStart(); + start(); }, function onFailure() { - wrappedStart(); + start(); }); }); - wrappedAsyncTest("complete_email_addition with XHR failure", function() { + asyncTest("complete_email_addition with XHR failure", function() { notificationCheck(network.completeEmailRegistration, "goodtoken"); }); - wrappedAsyncTest("complete_email_addition with XHR failure", function() { + asyncTest("complete_email_addition with XHR failure", function() { failureCheck(network.completeEmailRegistration, "goodtoken"); }); - wrappedAsyncTest("createUser with valid user", function() { + asyncTest("createUser with valid user", function() { network.createUser("validuser", "origin", function onSuccess(created) { ok(created); - wrappedStart(); + start(); }, function onFailure() { - wrappedStart(); + start(); }); }); - wrappedAsyncTest("createUser with invalid user", function() { + asyncTest("createUser with invalid user", function() { xhr.useResult("invalid"); network.createUser("invaliduser", "origin", function onSuccess(created) { equal(created, false); - wrappedStart(); + start(); }, function onFailure() { - wrappedStart(); + start(); }); }); - wrappedAsyncTest("createUser throttled", function() { + asyncTest("createUser throttled", function() { xhr.useResult("throttle"); network.createUser("validuser", "origin", function onSuccess(added) { equal(added, false, "throttled email returns onSuccess but with false as the value"); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("createUser with XHR failure", function() { + asyncTest("createUser with XHR failure", function() { notificationCheck(network.createUser, "validuser", "origin"); }); - wrappedAsyncTest("createUser with XHR failure", function() { + asyncTest("createUser with XHR failure", function() { failureCheck(network.createUser, "validuser", "origin"); }); - wrappedAsyncTest("checkUserRegistration with pending email", function() { + asyncTest("checkUserRegistration with pending email", function() { xhr.useResult("pending"); network.checkUserRegistration("registered@testuser.com", function(status) { equal(status, "pending"); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("checkUserRegistration with complete email", function() { + asyncTest("checkUserRegistration with complete email", function() { xhr.useResult("complete"); network.checkUserRegistration("registered@testuser.com", function(status) { equal(status, "complete"); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("checkUserRegistration with XHR failure", function() { + asyncTest("checkUserRegistration with XHR failure", function() { notificationCheck(network.checkUserRegistration, "registered@testuser.com"); }); - wrappedAsyncTest("checkUserRegistration with XHR failure", function() { + asyncTest("checkUserRegistration with XHR failure", function() { failureCheck(network.checkUserRegistration, "registered@testuser.com"); }); - wrappedAsyncTest("completeUserRegistration with valid token", function() { + asyncTest("completeUserRegistration with valid token", function() { network.completeUserRegistration("token", "password", function(registered) { ok(registered); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("completeUserRegistration with invalid token", function() { + asyncTest("completeUserRegistration with invalid token", function() { xhr.useResult("invalid"); network.completeUserRegistration("token", "password", function(registered) { equal(registered, false); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("completeUserRegistration with XHR failure", function() { + asyncTest("completeUserRegistration with XHR failure", function() { notificationCheck(network.completeUserRegistration, "token", "password"); }); - wrappedAsyncTest("completeUserRegistration with XHR failure", function() { + asyncTest("completeUserRegistration with XHR failure", function() { failureCheck(network.completeUserRegistration, "token", "password"); }); - wrappedAsyncTest("cancelUser valid", function() { + asyncTest("cancelUser valid", function() { network.cancelUser(function() { // XXX need a test here. ok(true); - wrappedStart(); + start(); }, function onFailure() { - wrappedStart(); + start(); }); }); - wrappedAsyncTest("cancelUser invalid", function() { + asyncTest("cancelUser invalid", function() { xhr.useResult("invalid"); network.cancelUser(function() { // XXX need a test here. ok(true); - wrappedStart(); + start(); }, function onFailure() { - wrappedStart(); + start(); }); }); - wrappedAsyncTest("cancelUser with XHR failure", function() { + asyncTest("cancelUser with XHR failure", function() { notificationCheck(network.cancelUser); }); - wrappedAsyncTest("cancelUser with XHR failure", function() { + asyncTest("cancelUser with XHR failure", function() { failureCheck(network.cancelUser); }); - wrappedAsyncTest("emailRegistered with taken email", function() { + asyncTest("emailRegistered with taken email", function() { network.emailRegistered("registered@testuser.com", function(taken) { equal(taken, true, "a taken email is marked taken"); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("emailRegistered with nottaken email", function() { + asyncTest("emailRegistered with nottaken email", function() { network.emailRegistered("unregistered@testuser.com", function(taken) { equal(taken, false, "a not taken email is not marked taken"); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("emailRegistered with XHR failure", function() { + asyncTest("emailRegistered with XHR failure", function() { notificationCheck(network.emailRegistered, "registered@testuser.com"); }); - wrappedAsyncTest("emailRegistered with XHR failure", function() { + asyncTest("emailRegistered with XHR failure", function() { failureCheck(network.emailRegistered, "registered@testuser.com"); }); - wrappedAsyncTest("addEmail valid", function() { + asyncTest("addEmail valid", function() { network.addEmail("address", "origin", function onSuccess(added) { ok(added); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("addEmail invalid", function() { + asyncTest("addEmail invalid", function() { xhr.useResult("invalid"); network.addEmail("address", "origin", function onSuccess(added) { equal(added, false); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("addEmail throttled", function() { + asyncTest("addEmail throttled", function() { xhr.useResult("throttle"); network.addEmail("address", "origin", function onSuccess(added) { equal(added, false, "throttled email returns onSuccess but with false as the value"); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("addEmail with XHR failure", function() { + asyncTest("addEmail with XHR failure", function() { notificationCheck(network.addEmail, "address", "origin"); }); - wrappedAsyncTest("addEmail with XHR failure", function() { + asyncTest("addEmail with XHR failure", function() { failureCheck(network.addEmail, "address", "origin"); }); - wrappedAsyncTest("checkEmailRegistration pending", function() { + asyncTest("checkEmailRegistration pending", function() { xhr.useResult("pending"); network.checkEmailRegistration("registered@testuser.com", function(status) { equal(status, "pending"); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("checkEmailRegistration complete", function() { + asyncTest("checkEmailRegistration complete", function() { xhr.useResult("complete"); network.checkEmailRegistration("registered@testuser.com", function(status) { equal(status, "complete"); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("checkEmailRegistration with XHR failure", function() { + asyncTest("checkEmailRegistration with XHR failure", function() { notificationCheck(network.checkEmailRegistration, "address"); }); - wrappedAsyncTest("checkEmailRegistration with XHR failure", function() { + asyncTest("checkEmailRegistration with XHR failure", function() { failureCheck(network.checkEmailRegistration, "address"); }); - wrappedAsyncTest("removeEmail valid", function() { + asyncTest("removeEmail valid", function() { network.removeEmail("validemail", function onSuccess() { // XXX need a test here; ok(true); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("removeEmail invalid", function() { + asyncTest("removeEmail invalid", function() { xhr.useResult("invalid"); network.removeEmail("invalidemail", function onSuccess() { // XXX need a test here; ok(true); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("removeEmail with XHR failure", function() { + asyncTest("removeEmail with XHR failure", function() { notificationCheck(network.removeEmail, "validemail"); }); - wrappedAsyncTest("removeEmail with XHR failure", function() { + asyncTest("removeEmail with XHR failure", function() { failureCheck(network.removeEmail, "invalidemail"); }); - wrappedAsyncTest("requestPasswordReset", function() { + asyncTest("requestPasswordReset", function() { network.requestPasswordReset("address", "origin", function onSuccess() { // XXX need a test here; ok(true); - wrappedStart(); + start(); }, function onFailure() { ok(false); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("requestPasswordReset with XHR failure", function() { + asyncTest("requestPasswordReset with XHR failure", function() { notificationCheck(network.requestPasswordReset, "address", "origin"); }); - wrappedAsyncTest("requestPasswordReset with XHR failure", function() { + asyncTest("requestPasswordReset with XHR failure", function() { failureCheck(network.requestPasswordReset, "address", "origin"); }); - wrappedAsyncTest("serverTime", function() { + asyncTest("resetPassword", function() { + network.resetPassword("password", function onSuccess() { + // XXX need a test here; + ok(true); + start(); + }, function onFailure() { + ok(false); + start(); + }); + + }); + + asyncTest("resetPassword with XHR failure", function() { + xhr.useResult("ajaxError"); +/* + the body of this function is not yet written + + network.resetPassword("password", function onSuccess() { + ok(false, "XHR failure should never call success"); + start(); + }, function onFailure() { + ok(true, "XHR failure should always call failure"); + start(); + }); +*/ + start(); + }); + + asyncTest("serverTime", function() { // I am forcing the server time to be 1.25 seconds off. xhr.setContextInfo("server_time", new Date().getTime() - 1250); network.serverTime(function onSuccess(time) { @@ -556,54 +578,89 @@ // time as it is on the server, which could be more than 100ms off of // what the local machine says it is. //equal(Math.abs(diff) < 100, true, "server time and local time should be less than 100ms different (is " + diff + "ms different)"); - wrappedStart(); + start(); }, function onfailure() { - wrappedStart(); + start(); }); }); - wrappedAsyncTest("serverTime with XHR failure before context has been setup", function() { + asyncTest("serverTime with XHR failure before context has been setup", function() { notificationCheck(); xhr.useResult("contextAjaxError"); network.serverTime(); }); - wrappedAsyncTest("serverTime with XHR failure before context has been setup", function() { + asyncTest("serverTime with XHR failure before context has been setup", function() { xhr.useResult("contextAjaxError"); network.serverTime(function onSuccess(time) { ok(false, "XHR failure should never call success"); - wrappedStart(); + start(); }, function onFailure() { ok(true, "XHR failure should always call failure"); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("codeVersion", function() { + asyncTest("codeVersion", function() { network.codeVersion(function onComplete(version) { equal(version, "ABC123", "version returned properly"); - wrappedStart(); + start(); }, function onFailure() { ok(false, "unexpected failure"); - wrappedStart(); + start(); }); }); - wrappedAsyncTest("codeVersion with XHR error", function() { + asyncTest("codeVersion with XHR error", function() { xhr.useResult("contextAjaxError"); network.codeVersion(function onComplete(version) { ok(false, "XHR failure should never call complete"); - wrappedStart(); + start(); }, function onFailure() { - ok(true, "XHR fialure should always return failure"); - wrappedStart(); + ok(true, "XHR failure should always return failure"); + start(); }); + }); + asyncTest("addressInfo with unknown secondary email", function() { + xhr.useResult("unknown_secondary"); + + network.addressInfo("testuser@testuser.com", function onComplete(data) { + equal(data.type, "secondary", "type is secondary"); + equal(data.known, false, "address is unknown to BrowserID"); + start(); + }, unexpectedFailure); + }); + + asyncTest("addressInfo with known seconday email", function() { + xhr.useResult("known_secondary"); + + network.addressInfo("testuser@testuser.com", function onComplete(data) { + equal(data.type, "secondary", "type is secondary"); + equal(data.known, true, "address is known to BrowserID"); + start(); + }, unexpectedFailure); + }); + + asyncTest("addressInfo with primary email", function() { + xhr.useResult("primary"); + + network.addressInfo("testuser@testuser.com", function onComplete(data) { + equal(data.type, "primary", "type is primary"); + ok("auth" in data, "auth field exists"); + ok("prov" in data, "prov field exists"); + start(); + }, unexpectedFailure); + }); + + asyncTest("addressInfo with XHR error", function() { + xhr.useResult("ajaxError"); + failureCheck(network.addressInfo, "testuser@testuser.com"); }); asyncTest("changePassword happy case, expect complete callback with true status", function() { diff --git a/resources/static/test/qunit/shared/user_unit_test.js b/resources/static/test/qunit/shared/user_unit_test.js index e90fd1c32..997c72399 100644 --- a/resources/static/test/qunit/shared/user_unit_test.js +++ b/resources/static/test/qunit/shared/user_unit_test.js @@ -44,7 +44,9 @@ var jwcert = require("./jwcert"); storage = bid.Storage, network = bid.Network, xhr = bid.Mocks.xhr, - testOrigin = "testOrigin"; + testOrigin = "https://browserid.org", + testHelpers = bid.TestHelpers, + provisioning = bid.Mocks.Provisioning // I generated these locally, they are used nowhere else. var pubkey = {"algorithm":"RS","n":"56063028070432982322087418176876748072035482898334811368408525596198252519267108132604198004792849077868951906170812540713982954653810539949384712773390200791949565903439521424909576832418890819204354729217207360105906039023299561374098942789996780102073071760852841068989860403431737480182725853899733706069","e":"65537"}; @@ -92,14 +94,11 @@ var jwcert = require("./jwcert"); module("shared/user", { setup: function() { - network.setXHR(xhr); - xhr.useResult("valid"); - lib.clearStoredEmailKeypairs(); + testHelpers.setup(); lib.setOrigin(testOrigin); - storage.site.remove(testOrigin, "email"); }, teardown: function() { - network.setXHR($); + testHelpers.teardown(); } }); @@ -116,11 +115,11 @@ var jwcert = require("./jwcert"); }); test("setOrigin, getHostname", function() { - var origin = "http://testorigin.com:10001"; + var origin = "http://browserid.org"; lib.setOrigin(origin); var hostname = lib.getHostname(); - equal(hostname, "testorigin.com", "getHostname returns only the hostname"); + equal(hostname, "browserid.org", "getHostname returns only the hostname"); }); test("getStoredEmailKeypairs", function() { @@ -156,6 +155,7 @@ var jwcert = require("./jwcert"); equal(0, count, "after clearing, there are no identities"); }); + /* asyncTest("createUser", function() { lib.createUser("testuser@testuser.com", function(status) { ok(status, "user created"); @@ -183,6 +183,70 @@ var jwcert = require("./jwcert"); start(); }); }); +*/ + asyncTest("createUser with unknown secondary happy case - expect 'secondary.verify'", function() { + xhr.useResult("unknown_secondary"); + + lib.createUser("unregistered@testuser.com", function(status) { + equal(status, "secondary.verify", "secondary user must be verified"); + start(); + }, failure("createUser failure")); + }); + + asyncTest("createUser with unknown secondary, throttled - expect status='secondary.could_not_add'", function() { + xhr.useResult("throttle"); + + lib.createUser("unregistered@testuser.com", function(status) { + equal(status, "secondary.could_not_add", "user creation refused"); + start(); + }, failure("createUser failure")); + }); + + asyncTest("createUser with unknown secondary, XHR failure - expect failure call", function() { + xhr.useResult("ajaxError"); + + lib.createUser("unregistered@testuser.com", + testHelpers.unexpectedSuccess, + testHelpers.expectXHRFailure + ); + }); + + asyncTest("createUser with primary, user verified with primary - expect 'primary.verified'", function() { + xhr.useResult("primary"); + provisioning.setSuccess(true); + + lib.createUser("unregistered@testuser.com", function(status) { + equal(status, "primary.verified", "primary user is already verified, correct status"); + start(); + }, testHelpers.unexpectedXHRFailure); + }); + + asyncTest("createUser with primary, user must authenticate with primary - expect 'primary.verify'", function() { + xhr.useResult("primary"); + + provisioning.setFailure({ + code: "MUST_AUTHENTICATE", + msg: "Wahhooo!!" + }); + + lib.createUser("unregistered@testuser.com", function(status) { + equal(status, "primary.verify", "primary must verify with primary, correct status"); + start(); + }, testHelpers.unexpectedXHRFailure); + }); + + asyncTest("createUser with primary, unknown provisioning failure, expect XHR failure callback", function() { + xhr.useResult("primary"); + provisioning.setFailure({ + code: "primaryError", + msg: "some error" + }); + + lib.createUser("unregistered@testuser.com", + testHelpers.unexpectedSuccess, + testHelpers.expectXHRFailure + ); + }); asyncTest("waitForUserValidation with `complete` response", function() { storage.setStagedOnBehalfOf(testOrigin); diff --git a/resources/static/test/qunit/testHelpers/helpers.js b/resources/static/test/qunit/testHelpers/helpers.js index aa0601edd..61890240c 100644 --- a/resources/static/test/qunit/testHelpers/helpers.js +++ b/resources/static/test/qunit/testHelpers/helpers.js @@ -2,12 +2,15 @@ var bid = BrowserID, mediator = bid.Mediator, network = bid.Network, + user = bid.User, storage = bid.Storage, xhr = bid.Mocks.xhr, + provisioning = bid.Mocks.Provisioning, screens = bid.Screens, tooltip = bid.Tooltip, registrations = []; - calls = {}; + calls = {}, + testOrigin = "https://browserid.org"; function register(message, cb) { registrations.push(mediator.subscribe(message, function(msg, info) { @@ -44,13 +47,20 @@ var el = $("#controller_head"); el.find("#formWrap .contents").html(""); el.find("#wait .contents").html(""); - $("#error").html("<div class='contents'></div>").hide(); - + $(".error").removeClass("error"); + $("#error").stop().html("<div class='contents'></div>").hide(); + $(".notification").stop().hide(); unregisterAll(); mediator.reset(); screens.wait.hide(); screens.error.hide(); tooltip.reset(); + provisioning.setSuccess(false); + provisioning.setFailure(false); + user.init({ + provisioning: provisioning + }); + user.setOrigin(testOrigin); }, teardown: function() { @@ -58,16 +68,38 @@ mediator.reset(); network.setXHR($); storage.clear(); - $("#error").html("<div class='contents'></div>").hide(); + $(".error").removeClass("error"); + $("#error").stop().html("<div class='contents'></div>").hide(); + $(".notification").stop().hide(); screens.wait.hide(); screens.error.hide(); tooltip.reset(); + provisioning.setSuccess(false); + provisioning.setFailure(false); + user.reset(); }, register: register, errorVisible: function() { return screens.error.visible; }, - checkNetworkError: checkNetworkError + testErrorVisible: function() { + equal(this.errorVisible(), true, "error screen is visible"); + }, + checkNetworkError: checkNetworkError, + unexpectedSuccess: function() { + ok(false, "unexpected success"); + start(); + }, + + expectXHRFailure: function() { + ok(true, "expected XHR failure"); + start(); + }, + + unexpectedXHRFailure: function() { + ok(false, "unexpected XHR failure"); + start(); + } }; }()); diff --git a/resources/views/signup.ejs b/resources/views/signup.ejs index 1578755fa..26997d3c1 100644 --- a/resources/views/signup.ejs +++ b/resources/views/signup.ejs @@ -27,6 +27,15 @@ </p> </li> + <li class="notification" id="congrats"> + <p class="serif"> + <strong id="email">Your address</strong> has been verified! + + <p class="siteinfo"> + Your new address is set up! + </p> + </p> + </li> </ul> <ul class="inputs forminputs"> -- GitLab