From 5e69945543668a8df9fd1568e9a21e608dea00aa Mon Sep 17 00:00:00 2001
From: Shane Tomlinson <stomlinson@mozilla.com>
Date: Fri, 13 Apr 2012 16:09:45 +0100
Subject: [PATCH] Fully bring in reset password using the set_password
 controller.

* Remove the forgot_password controller, templates, tests, etc.
* Update authenticate to trigger "new_user" with rehydration info
* Update set_password to be able to handle resetting passwords as well.
* Update the state machine to handle password setting or resetting.
---
 lib/static_resources.js                       |  1 -
 .../static/dialog/controllers/actions.js      |  7 +-
 .../static/dialog/controllers/authenticate.js |  2 +-
 .../dialog/controllers/forgot_password.js     | 49 ------------
 .../static/dialog/controllers/set_password.js |  9 ++-
 resources/static/dialog/resources/helpers.js  | 22 +++---
 resources/static/dialog/resources/state.js    | 79 +++++++++++--------
 resources/static/dialog/start.js              |  1 -
 .../static/dialog/views/forgot_password.ejs   | 30 -------
 .../static/dialog/views/set_password.ejs      | 15 +++-
 resources/static/shared/history.js            |  1 +
 .../static/shared/modules/page_module.js      |  3 +
 resources/static/shared/network.js            |  9 ++-
 resources/static/shared/state_machine.js      |  1 +
 resources/static/shared/user.js               |  7 +-
 .../static/test/cases/controllers/actions.js  | 42 +++++++++-
 .../test/cases/controllers/authenticate.js    |  7 +-
 .../test/cases/controllers/forgot_password.js |  6 +-
 .../test/cases/controllers/set_password.js    |  2 +-
 .../static/test/cases/resources/helpers.js    | 16 ++--
 .../static/test/cases/resources/state.js      | 23 ++++--
 resources/static/test/cases/shared/network.js | 19 +++--
 resources/static/test/cases/shared/user.js    | 14 ++--
 resources/static/test/testHelpers/helpers.js  |  2 +-
 resources/views/test.ejs                      |  2 -
 25 files changed, 183 insertions(+), 186 deletions(-)
 delete mode 100644 resources/static/dialog/controllers/forgot_password.js
 delete mode 100644 resources/static/dialog/views/forgot_password.ejs

diff --git a/lib/static_resources.js b/lib/static_resources.js
index 26e7b85da..e387b3748 100644
--- a/lib/static_resources.js
+++ b/lib/static_resources.js
@@ -89,7 +89,6 @@ var dialog_js = und.flatten([
     '/dialog/controllers/actions.js',
     '/dialog/controllers/dialog.js',
     '/dialog/controllers/authenticate.js',
-    '/dialog/controllers/forgot_password.js',
     '/dialog/controllers/check_registration.js',
     '/dialog/controllers/pick_email.js',
     '/dialog/controllers/add_email.js',
diff --git a/resources/static/dialog/controllers/actions.js b/resources/static/dialog/controllers/actions.js
index c674f1dc9..b0142af79 100644
--- a/resources/static/dialog/controllers/actions.js
+++ b/resources/static/dialog/controllers/actions.js
@@ -78,8 +78,7 @@ BrowserID.Modules.Actions = (function() {
     },
 
     doStageUser: function(info) {
-      var email = info.email;
-      bid.Helpers.Dialog.createUser.call(this, email);
+      dialogHelpers.createUser.call(this, info.email, info.password, info.ready);
     },
 
     doConfirmUser: function(info) {
@@ -103,11 +102,11 @@ BrowserID.Modules.Actions = (function() {
     },
 
     doForgotPassword: function(info) {
-      startService("forgot_password", info);
+      startService("set_password", _.extend(info, { password_reset: true }));
     },
 
     doResetPassword: function(info) {
-      this.doConfirmUser(info);
+      dialogHelpers.resetPassword.call(this, info.email, info.password, info.ready);
     },
 
     doConfirmEmail: function(info) {
diff --git a/resources/static/dialog/controllers/authenticate.js b/resources/static/dialog/controllers/authenticate.js
index 692c62e3e..d81fc9db3 100644
--- a/resources/static/dialog/controllers/authenticate.js
+++ b/resources/static/dialog/controllers/authenticate.js
@@ -71,7 +71,7 @@ BrowserID.Modules.Authenticate = (function() {
         email = getEmail();
 
     if (email) {
-      self.close("new_user", { email: email });
+      self.close("new_user", { email: email }, { email: email });
     } else {
       complete(callback);
     }
diff --git a/resources/static/dialog/controllers/forgot_password.js b/resources/static/dialog/controllers/forgot_password.js
deleted file mode 100644
index 268f72417..000000000
--- a/resources/static/dialog/controllers/forgot_password.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
-/*global BrowserID:true, PageController: true */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-BrowserID.Modules.ForgotPassword = (function() {
-  "use strict";
-
-  var ANIMATION_TIME = 250,
-      bid = BrowserID,
-      helpers = bid.Helpers,
-      dialogHelpers = helpers.Dialog,
-      dom = bid.DOM;
-
-  function resetPassword() {
-    var self=this;
-    dialogHelpers.resetPassword.call(self, self.email);
-  }
-
-  function cancelResetPassword() {
-    this.close("cancel_state", { email: this.email });
-  }
-
-  var Module = bid.Modules.PageModule.extend({
-    start: function(options) {
-      var self=this;
-      self.email = options.email;
-      self.renderDialog("forgot_password", {
-        email: options.email || "",
-        requiredEmail: options.requiredEmail
-      });
-
-      self.click("#cancel", cancelResetPassword);
-
-      Module.sc.start.call(self, options);
-    },
-
-    submit: resetPassword
-
-    // BEGIN TESTING API
-    ,
-    resetPassword: resetPassword,
-    cancelResetPassword: cancelResetPassword
-    // END TESTING API
-  });
-
-  return Module;
-
-}());
diff --git a/resources/static/dialog/controllers/set_password.js b/resources/static/dialog/controllers/set_password.js
index ac0a24cb7..308cee328 100644
--- a/resources/static/dialog/controllers/set_password.js
+++ b/resources/static/dialog/controllers/set_password.js
@@ -7,16 +7,19 @@ BrowserID.Modules.SetPassword = (function() {
   "use strict";
   var bid = BrowserID,
       dom = bid.DOM,
-      complete = bid.Helpers.complete,
+      helpers = bid.Helpers,
+      complete = helpers.complete,
+      dialogHelpers = helpers.Dialog,
       sc;
 
   function submit(callback) {
     var pass = dom.getInner("#password"),
-        vpass = dom.getInner("#vpassword");
+        vpass = dom.getInner("#vpassword"),
+        options = this.options;
 
     var valid = bid.Validation.passwordAndValidationPassword(pass, vpass);
     if(valid) {
-      this.close("password_set", { password: pass });
+      this.publish("password_set", { password: pass });
     }
 
     complete(callback, valid);
diff --git a/resources/static/dialog/resources/helpers.js b/resources/static/dialog/resources/helpers.js
index 12029173c..46f771258 100644
--- a/resources/static/dialog/resources/helpers.js
+++ b/resources/static/dialog/resources/helpers.js
@@ -39,7 +39,7 @@
     user.getAssertion(email, user.getOrigin(), function(assert) {
       assert = assert || null;
       wait.hide();
-      self.close("assertion_generated", {
+      self.publish("assertion_generated", {
         assertion: assert
       });
 
@@ -58,28 +58,28 @@
       }, self.getErrorDialog(errors.authenticate, callback));
   }
 
-  function createUser(email, callback) {
+  function createUser(email, password, callback) {
     var self=this;
-    user.createSecondaryUser(email, function(status) {
+    user.createSecondaryUser(email, password, function(status) {
       if (status) {
         var info = { email: email };
-        self.close("user_staged", info, info);
+        self.publish("user_staged", info, info);
         complete(callback, true);
       }
       else {
+        // XXX will this tooltip ever be shown, the authentication screen has
+        // already been torn down by this point?
         tooltip.showTooltip("#could_not_add");
         complete(callback, false);
       }
     }, self.getErrorDialog(errors.createUser, callback));
   }
 
-  function resetPassword(email, callback) {
+  function resetPassword(email, password, callback) {
     var self=this;
-    user.requestPasswordReset(email, function(status) {
+    user.requestPasswordReset(email, password, function(status) {
       if (status.success) {
-        self.close("reset_password", {
-          email: email
-        });
+        self.publish("password_reset", { email: email });
       }
       else {
         tooltip.showTooltip("#could_not_add");
@@ -100,14 +100,14 @@
       user.addressInfo(email, function(info) {
         if (info.type === "primary") {
           var info = _.extend(info, { email: email, add: true });
-          self.close("primary_user", info, info);
+          self.publish("primary_user", info, info);
           complete(callback, true);
         }
         else {
           user.addEmail(email, function(added) {
             if (added) {
               var info = { email: email };
-              self.close("email_staged", info, info );
+              self.publish("email_staged", info, info );
             }
             else {
               tooltip.showTooltip("#could_not_add");
diff --git a/resources/static/dialog/resources/state.js b/resources/static/dialog/resources/state.js
index 2103ffd00..909993abd 100644
--- a/resources/static/dialog/resources/state.js
+++ b/resources/static/dialog/resources/state.js
@@ -20,7 +20,13 @@ BrowserID.State = (function() {
 
   function startStateMachine() {
     var self = this,
-        handleState = self.subscribe.bind(self),
+        handleState = function(msg, callback) {
+          self.subscribe(msg, function(msg, info) {
+            // This level of indirection is to ensure an info object is
+            // always present in the handler.
+            callback(msg, info || {});
+          });
+        },
         redirectToState = mediator.publish.bind(mediator),
         startAction = function(save, msg, options) {
           if (typeof save !== "boolean") {
@@ -35,8 +41,6 @@ BrowserID.State = (function() {
         cancelState = self.popState.bind(self);
 
     handleState("start", function(msg, info) {
-      info = info || {};
-
       self.hostname = info.hostname;
       self.privacyURL = info.privacyURL;
       self.tosURL = info.tosURL;
@@ -87,7 +91,6 @@ BrowserID.State = (function() {
     });
 
     handleState("authenticate", function(msg, info) {
-      info = info || {};
       info.privacyURL = self.privacyURL;
       info.tosURL = self.tosURL;
       startAction("doAuthenticate", info);
@@ -95,14 +98,26 @@ BrowserID.State = (function() {
 
     handleState("new_user", function(msg, info) {
       self.newUserEmail = info.email;
-      startAction("doSetPassword", info);
+      startAction(false, "doSetPassword", info);
     });
 
     handleState("password_set", function(msg, info) {
-      info = info || {};
-      info.email = self.newUserEmail;
-
-      startAction("doStageUser", info);
+      /* A password can be set for one of three reasons - 1) This is a new user
+       * or 2) a user is adding the first secondary address to an account that
+       * consists only of primary addresses, or 3) an existing user has
+       * forgotten their password and wants to reset it.  #1 is taken care of
+       * by newUserEmail, #3 is taken care of by resetPasswordEmail.
+       */
+      info = _.extend({ email: self.newUserEmail || self.resetPasswordEmail }, info);
+
+      if(self.newUserEmail) {
+        self.newUserEmail = null;
+        startAction(false, "doStageUser", info);
+      }
+      else if(self.resetPasswordEmail) {
+        self.resetPasswordEmail = null;
+        startAction(false, "doResetPassword", info);
+      }
     });
 
     handleState("user_staged", function(msg, info) {
@@ -133,7 +148,6 @@ BrowserID.State = (function() {
     });
 
     handleState("primary_user_provisioned", function(msg, info) {
-      info = info || {};
       info.add = !!addPrimaryUser;
       // The user is is authenticated with their IdP. Two possibilities exist
       // for the email - 1) create a new account or 2) add address to the
@@ -143,7 +157,7 @@ BrowserID.State = (function() {
     });
 
     handleState("primary_user_unauthenticated", function(msg, info) {
-      info = helpers.extend(info || {}, {
+      info = helpers.extend(info, {
         add: !!addPrimaryUser,
         email: email,
         requiredEmail: !!requiredEmail,
@@ -191,8 +205,6 @@ BrowserID.State = (function() {
     });
 
     handleState("email_chosen", function(msg, info) {
-      info = info || {};
-
       var email = info.email,
           idInfo = storage.getEmail(email);
 
@@ -283,6 +295,25 @@ BrowserID.State = (function() {
       startAction("doGenerateAssertion", info);
     });
 
+    handleState("forgot_password", function(msg, info) {
+      // User has forgotten their password, let them reset it.  The response
+      // message from the forgot_password controller will be a set_password.
+      // the set_password handler needs to know the forgotPassword email so it
+      // knows how to handle the password being set.  When the password is
+      // finally reset, the password_reset message will be raised where we must
+      // await email confirmation.
+      self.resetPasswordEmail = info.email;
+      startAction(false, "doForgotPassword", info);
+    });
+
+    handleState("password_reset", function(msg, info) {
+      // password_reset says the user has confirmed that they want to
+      // reset their password.  doResetPassword will attempt to invoke
+      // the create_user wsapi.  If the wsapi call is successful,
+      // the user will be shown the "go verify your account" message.
+      redirectToState("user_staged", info);
+    });
+
     handleState("assertion_generated", function(msg, info) {
       self.success = true;
       if (info.assertion !== null) {
@@ -307,26 +338,8 @@ BrowserID.State = (function() {
       redirectToState("email_chosen", info);
     });
 
-    handleState("forgot_password", function(msg, info) {
-      // forgot password initiates the forgotten password flow.
-      startAction(false, "doForgotPassword", info);
-    });
-
-    handleState("reset_password", function(msg, info) {
-      info = info || {};
-      // reset_password says the user has confirmed that they want to
-      // reset their password.  doResetPassword will attempt to invoke
-      // the create_user wsapi.  If the wsapi call is successful,
-      // the user will be shown the "go verify your account" message.
-
-      // We have to save the staged email address here for when the user
-      // verifies their account and user_confirmed is called.
-      self.stagedEmail = info.email;
-      startAction(false, "doResetPassword", info);
-    });
-
     handleState("add_email", function(msg, info) {
-      info = helpers.extend(info || {}, {
+      info = helpers.extend(info, {
         privacyURL: self.privacyURL,
         tosURL: self.tosURL
       });
@@ -341,7 +354,7 @@ BrowserID.State = (function() {
     });
 
     handleState("email_confirmed", function() {
-      redirectToState("email_chosen", { email: self.stagedEmail} );
+      redirectToState("email_chosen", { email: self.stagedEmail } );
     });
 
     handleState("cancel_state", function(msg, info) {
diff --git a/resources/static/dialog/start.js b/resources/static/dialog/start.js
index 14cfe1cf3..49c7c439b 100644
--- a/resources/static/dialog/start.js
+++ b/resources/static/dialog/start.js
@@ -23,7 +23,6 @@
       moduleManager.register("add_email", modules.AddEmail);
       moduleManager.register("authenticate", modules.Authenticate);
       moduleManager.register("check_registration", modules.CheckRegistration);
-      moduleManager.register("forgot_password", modules.ForgotPassword);
       moduleManager.register("is_this_your_computer", modules.IsThisYourComputer);
       moduleManager.register("pick_email", modules.PickEmail);
       moduleManager.register("required_email", modules.RequiredEmail);
diff --git a/resources/static/dialog/views/forgot_password.ejs b/resources/static/dialog/views/forgot_password.ejs
deleted file mode 100644
index 3bbd7cd8e..000000000
--- a/resources/static/dialog/views/forgot_password.ejs
+++ /dev/null
@@ -1,30 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-  <strong>
-  <% if (requiredEmail) { %>
-    <%= gettext('The site requested you sign in using') %>
-  <% } else { %>
-    <%= gettext('Sign in using') %>
-  <% } %>
-  </strong>
-
-  <div class="form_section">
-      <ul class="inputs">
-          <li>
-              <label for="email" class="serif"><%= gettext('Email') %></label>
-              <input id="email" class="sans" type="email" value="<%= email %>" disabled />
-
-              <div id="could_not_add" class="tooltip" for="email">
-                <%= gettext('We just sent an email to that address! If you really want to send another, wait a minute or two and try again.') %>
-              </div>
-          </li>
-
-      </ul>
-
-      <div class="submit cf">
-          <button tabindex="1"><%= gettext('reset password') %></button>
-          <a href="#" id="cancel" class="action" tabindex="2"><%= gettext('cancel') %></a>
-      </div>
-  </div>
diff --git a/resources/static/dialog/views/set_password.ejs b/resources/static/dialog/views/set_password.ejs
index b970090ff..c57943f4d 100644
--- a/resources/static/dialog/views/set_password.ejs
+++ b/resources/static/dialog/views/set_password.ejs
@@ -6,9 +6,12 @@
 
   <div class="form_section" id="set_password">
       <ul class="inputs">
-          <li>
-              <%= gettext("This email looks new, so let's get you set up.") %>
-          </li>
+          <% if(!password_reset) { %>
+            <li>
+                <%= gettext("This email looks new, so let's get you set up.") %>
+            </li>
+          <% } %>
+
 
           <li>
               <label for="password" class="serif"><%= gettext('Password') %></label>
@@ -21,6 +24,10 @@
               <div class="tooltip" id="password_length" for="password">
                 <%= gettext('Password must be between 8 and 80 characters long.') %>
               </div>
+
+              <div id="could_not_add" class="tooltip" for="password">
+                <%= gettext('We just sent an email to that address! If you really want to send another, wait a minute or two and try again.') %>
+              </div>
           </li>
 
           <li>
@@ -42,6 +49,6 @@
           <button tabindex="1" id="<%= password_reset ? "password_reset" : "verify_user" %>">
             <%= password_reset ? gettext('reset password') : gettext('verify email') %>
           </button>
-          <a id="cancel" href="#"><%= gettext('cancel') %></a>
+          <a id="cancel" class="action" href="#"><%= gettext('cancel') %></a>
       </div>
   </div>
diff --git a/resources/static/shared/history.js b/resources/static/shared/history.js
index 6cbaa68fe..8c7653c3a 100644
--- a/resources/static/shared/history.js
+++ b/resources/static/shared/history.js
@@ -29,6 +29,7 @@ BrowserID.History = (function() {
       return this.current;
     },
 
+    // XXX this should be renamed to pushState
     saveState: function() {
       this.history.push(this.current);
     },
diff --git a/resources/static/shared/modules/page_module.js b/resources/static/shared/modules/page_module.js
index 97936af09..1de6032ac 100644
--- a/resources/static/shared/modules/page_module.js
+++ b/resources/static/shared/modules/page_module.js
@@ -51,6 +51,8 @@ BrowserID.Modules.PageModule = (function() {
 
     start: function(options) {
       var self=this;
+      self.options = options || {};
+
       self.bind("form", "submit", cancelEvent(onSubmit));
       // TODO - why is this here and not in pick_email?
       self.click("#thisIsNotMe", self.close.bind(self, "notme"));
@@ -154,6 +156,7 @@ BrowserID.Modules.PageModule = (function() {
     submit: function() {
     },
 
+    // XXX maybe we should not get rid of this.
     close: function(message) {
       this.destroy();
       if (message) {
diff --git a/resources/static/shared/network.js b/resources/static/shared/network.js
index 9b11a7039..478531dcb 100644
--- a/resources/static/shared/network.js
+++ b/resources/static/shared/network.js
@@ -321,14 +321,15 @@ BrowserID.Network = (function() {
     /**
      * Request a password reset for the given email address.
      * @method requestPasswordReset
-     * @param {string} email - email address to reset password for.
+     * @param {string} email
+     * @param {string} password
+     * @param {string} origin
      * @param {function} [onComplete] - Callback to call when complete.
      * @param {function} [onFailure] - Called on XHR failure.
      */
-    requestPasswordReset: function(email, origin, onComplete, onFailure) {
+    requestPasswordReset: function(email, password, origin, onComplete, onFailure) {
       if (email) {
-        // XXX we need a password!
-        Network.createUser(email, origin, "", onComplete, onFailure);
+        Network.createUser(email, password, origin, onComplete, onFailure);
       } else {
         // TODO: if no email is provided, then what?
         throw "no email provided to password reset";
diff --git a/resources/static/shared/state_machine.js b/resources/static/shared/state_machine.js
index 1b2c90b37..c6dcafdcc 100644
--- a/resources/static/shared/state_machine.js
+++ b/resources/static/shared/state_machine.js
@@ -53,6 +53,7 @@ BrowserID.StateMachine = (function() {
       // only save the current state when a new state comes in.
       var cmd = history.getCurrent();
       if(cmd && cmd.save) {
+        // XXX saveState should be renamed to pushState
         history.saveState();
       }
 
diff --git a/resources/static/shared/user.js b/resources/static/shared/user.js
index 1ea183078..fe76e8c11 100644
--- a/resources/static/shared/user.js
+++ b/resources/static/shared/user.js
@@ -599,17 +599,18 @@ BrowserID.User = (function() {
     /**
      * Request a password reset for the given email address.
      * @method requestPasswordReset
-     * @param {string} email - email address to reset password for.
+     * @param {string} email
+     * @param {string} password
      * @param {function} [onComplete] - Callback to call when complete, called
      * with a single object, info.
      *    info.status {boolean} - true or false whether request was successful.
      *    info.reason {string} - if status false, reason of failure.
      * @param {function} [onFailure] - Called on XHR failure.
      */
-    requestPasswordReset: function(email, onComplete, onFailure) {
+    requestPasswordReset: function(email, password, onComplete, onFailure) {
       User.isEmailRegistered(email, function(registered) {
         if (registered) {
-          network.requestPasswordReset(email, origin, function(reset) {
+          network.requestPasswordReset(email, password, origin, function(reset) {
             var status = {
               success: reset
             };
diff --git a/resources/static/test/cases/controllers/actions.js b/resources/static/test/cases/controllers/actions.js
index 0e0881e27..488639009 100644
--- a/resources/static/test/cases/controllers/actions.js
+++ b/resources/static/test/cases/controllers/actions.js
@@ -84,7 +84,7 @@
   asyncTest("doCannotVerifyRequiredPrimary - show the error screen", function() {
     createController({
       ready: function() {
-        controller.doCannotVerifyRequiredPrimary({ email: "testuser@testuser.com"});
+        controller.doCannotVerifyRequiredPrimary({ email: TEST_EMAIL});
 
         testHelpers.testErrorVisible();
         start();
@@ -112,5 +112,45 @@
     testActionStartsModule('doGenerateAssertion', { email: TEST_EMAIL }, "generate_assertion");
   });
 
+  asyncTest("doEmailChosen - start the email_chosen service", function() {
+    testActionStartsModule('doEmailChosen', { email: TEST_EMAIL }, "email_chosen");
+  });
+
+
+  asyncTest("doEmailConfirmed - generate an assertion for the email", function() {
+    createController({
+      ready: function() {
+        testHelpers.register("assertion_generated", function(msg, info) {
+          ok(info.assertion, "assertion generated");
+          start();
+        });
+
+        user.syncEmailKeypair(TEST_EMAIL, function() {
+          controller.doEmailConfirmed({email: TEST_EMAIL});
+        });
+      }
+    });
+  });
+
+  asyncTest("doStageUser with successful creation - trigger user_staged", function() {
+    createController({
+      ready: function() {
+        var email;
+        testHelpers.register("user_staged", function(msg, info) {
+          email = info.email;
+        });
+
+        controller.doStageUser({ email: TEST_EMAIL, password: "password", ready: function(status) {
+          equal(status, true, "correct status");
+          equal(email, TEST_EMAIL, "user successfully staged");
+          start();
+        }});
+      }
+    });
+  });
+
+  asyncTest("doForgotPassword - call the set_password controller with reset_password true", function() {
+    testActionStartsModule('doForgotPassword', { email: TEST_EMAIL }, "set_password");
+  });
 }());
 
diff --git a/resources/static/test/cases/controllers/authenticate.js b/resources/static/test/cases/controllers/authenticate.js
index 91cbe5e6e..653a7bd19 100644
--- a/resources/static/test/cases/controllers/authenticate.js
+++ b/resources/static/test/cases/controllers/authenticate.js
@@ -80,8 +80,11 @@
   });
 
   function testUserUnregistered() {
-    register("new_user", function() {
-      ok(true, "email was valid, user not registered");
+    register("new_user", function(msg, info, rehydrate) {
+      ok(info.email, "new_user triggered with info.email");
+      // rehydration email used to go back to authentication controller if
+      // the user cancels one of the next steps.
+      ok(rehydrate.email, "new_user triggered with rehydrate.email");
       start();
     });
 
diff --git a/resources/static/test/cases/controllers/forgot_password.js b/resources/static/test/cases/controllers/forgot_password.js
index 77c24dd89..d81d8b1de 100644
--- a/resources/static/test/cases/controllers/forgot_password.js
+++ b/resources/static/test/cases/controllers/forgot_password.js
@@ -40,9 +40,9 @@
     equal($("#email").val(), "registered@testuser.com", "email prefilled");
   });
 
-  asyncTest("resetPassword raises 'reset_password' with email address", function() {
-    register("reset_password", function(msg, info) {
-      equal(info.email, "registered@testuser.com", "reset_password raised with correct email address");
+  asyncTest("resetPassword raises 'password_reset' with email address", function() {
+    register("password_reset", function(msg, info) {
+      equal(info.email, "registered@testuser.com", "password_reset raised with correct email address");
       start();
     });
 
diff --git a/resources/static/test/cases/controllers/set_password.js b/resources/static/test/cases/controllers/set_password.js
index 3de244dbd..bfdda1c0b 100644
--- a/resources/static/test/cases/controllers/set_password.js
+++ b/resources/static/test/cases/controllers/set_password.js
@@ -53,7 +53,7 @@
     });
 
     controller.submit(function() {
-      ok(password, "password", "password_set message raised with correct password");
+      equal(password, "password", "password_set message raised with correct password");
       start();
     });
   });
diff --git a/resources/static/test/cases/resources/helpers.js b/resources/static/test/cases/resources/helpers.js
index 279f7dee8..4a80ad29e 100644
--- a/resources/static/test/cases/resources/helpers.js
+++ b/resources/static/test/cases/resources/helpers.js
@@ -21,7 +21,7 @@
       badError = testHelpers.unexpectedXHRFailure;
 
   var controllerMock = {
-    close: function(message, info) {
+    publish: function(message, info) {
       closeCB && closeCB(message, info);
     },
 
@@ -116,7 +116,7 @@
     xhr.useResult("unknown_secondary");
     closeCB = expectedClose("user_staged", "email", "unregistered@testuser.com");
 
-    dialogHelpers.createUser.call(controllerMock, "unregistered@testuser.com", function(staged) {
+    dialogHelpers.createUser.call(controllerMock, "unregistered@testuser.com", "password", function(staged) {
       equal(staged, true, "user was staged");
       start();
     });
@@ -126,7 +126,7 @@
     closeCB = badClose;
 
     xhr.useResult("throttle");
-    dialogHelpers.createUser.call(controllerMock, "unregistered@testuser.com", function(staged) {
+    dialogHelpers.createUser.call(controllerMock, "unregistered@testuser.com", "password", function(staged) {
       equal(staged, false, "user was not staged");
       start();
     });
@@ -136,7 +136,7 @@
     errorCB = expectedError;
 
     xhr.useResult("ajaxError");
-    dialogHelpers.createUser.call(controllerMock, "registered@testuser.com", testHelpers.unexpectedSuccess);
+    dialogHelpers.createUser.call(controllerMock, "registered@testuser.com", "password", testHelpers.unexpectedSuccess);
   });
 
   asyncTest("addEmail with primary email happy case, expects primary_user message", function() {
@@ -185,8 +185,8 @@
   });
 
   asyncTest("resetPassword happy case", function() {
-    closeCB = expectedClose("reset_password", "email", "registered@testuser.com");
-    dialogHelpers.resetPassword.call(controllerMock, "registered@testuser.com", function(reset) {
+    closeCB = expectedClose("password_reset", "email", "registered@testuser.com");
+    dialogHelpers.resetPassword.call(controllerMock, "registered@testuser.com", "password", function(reset) {
       ok(reset, "password reset");
       start();
     });
@@ -195,7 +195,7 @@
 
   asyncTest("resetPassword throttled", function() {
     xhr.useResult("throttle");
-    dialogHelpers.resetPassword.call(controllerMock, "registered@testuser.com", function(reset) {
+    dialogHelpers.resetPassword.call(controllerMock, "registered@testuser.com", "password", function(reset) {
       equal(reset, false, "password not reset");
       start();
     });
@@ -205,7 +205,7 @@
     errorCB = expectedError;
 
     xhr.useResult("ajaxError");
-    dialogHelpers.resetPassword.call(controllerMock, "registered@testuser.com", function(reset) {
+    dialogHelpers.resetPassword.call(controllerMock, "registered@testuser.com", "password", function(reset) {
       ok(false, "unexpected close");
       start();
     });
diff --git a/resources/static/test/cases/resources/state.js b/resources/static/test/cases/resources/state.js
index 4ce48b1ba..f1981a196 100644
--- a/resources/static/test/cases/resources/state.js
+++ b/resources/static/test/cases/resources/state.js
@@ -84,6 +84,15 @@
     equal(actions.info.doSetPassword.email, TEST_EMAIL, "correct email sent to doSetPassword");
   });
 
+  test("cancel new user password_set flow - go back to the authentication screen", function() {
+    mediator.publish("authenticate");
+    mediator.publish("new_user", undefined, { email: TEST_EMAIL });
+    mediator.publish("password_set");
+    actions.info.doAuthenticate = {};
+    mediator.publish("cancel_state");
+    equal(actions.info.doAuthenticate.email, TEST_EMAIL, "authenticate called with the correct email");
+  });
+
   test("password_set - call doStageUser with correct email", function() {
     mediator.publish("new_user", { email: TEST_EMAIL });
     mediator.publish("password_set");
@@ -223,13 +232,13 @@
     equal(actions.info.doForgotPassword.requiredEmail, true, "correct requiredEmail passed");
   });
 
-  test("reset_password to user_confirmed - call doResetPassword then doEmailConfirmed", function() {
-    // reset_password indicates the user has verified that they want to reset
+  test("password_reset to user_confirmed - call doUserStaged then doEmailConfirmed", function() {
+    // password_reset indicates the user has verified that they want to reset
     // their password.
-    mediator.publish("reset_password", {
+    mediator.publish("password_reset", {
       email: TEST_EMAIL
     });
-    equal(actions.info.doResetPassword.email, TEST_EMAIL, "reset password with the correct email");
+    equal(actions.info.doConfirmUser.email, TEST_EMAIL, "doConfirmUser with the correct email");
 
     // At this point the user should be displayed the "go confirm your address"
     // screen.
@@ -247,13 +256,13 @@
   });
 
 
-  test("cancel reset_password flow - go two steps back", function() {
+  test("cancel password_reset flow - go two steps back", function() {
     // we want to skip the "verify" screen of reset password and instead go two
     // screens back.  Do do this, we are simulating the steps necessary to get
-    // to the reset_password flow.
+    // to the password_reset flow.
     mediator.publish("authenticate");
     mediator.publish("forgot_password", undefined, { email: TEST_EMAIL });
-    mediator.publish("reset_password", { email: TEST_EMAIL });
+    mediator.publish("password_reset");
     actions.info.doAuthenticate = {};
     mediator.publish("cancel_state");
     equal(actions.info.doAuthenticate.email, TEST_EMAIL, "authenticate called with the correct email");
diff --git a/resources/static/test/cases/shared/network.js b/resources/static/test/cases/shared/network.js
index b5a501960..4aea2350f 100644
--- a/resources/static/test/cases/shared/network.js
+++ b/resources/static/test/cases/shared/network.js
@@ -327,7 +327,7 @@
 
 
   asyncTest("addSecondaryEmail valid", function() {
-    network.addSecondaryEmail("address", "origin", function onSuccess(added) {
+    network.addSecondaryEmail("testuser@testuser.com", "origin", function onSuccess(added) {
       ok(added);
       start();
     }, testHelpers.unexpectedFailure);
@@ -335,7 +335,7 @@
 
   asyncTest("addSecondaryEmail invalid", function() {
     transport.useResult("invalid");
-    network.addSecondaryEmail("address", "origin", function onSuccess(added) {
+    network.addSecondaryEmail("testuser@testuser.com", "origin", function onSuccess(added) {
       equal(added, false);
       start();
     }, testHelpers.unexpectedFailure);
@@ -344,14 +344,14 @@
   asyncTest("addSecondaryEmail throttled", function() {
     transport.useResult("throttle");
 
-    network.addSecondaryEmail("address", "origin", function onSuccess(added) {
+    network.addSecondaryEmail("testuser@testuser.com", "origin", function onSuccess(added) {
       equal(added, false, "throttled email returns onSuccess but with false as the value");
       start();
     }, testHelpers.unexpectedFailure);
   });
 
   asyncTest("addSecondaryEmail with XHR failure", function() {
-    failureCheck(network.addSecondaryEmail, "address", "origin");
+    failureCheck(network.addSecondaryEmail, "testuser@testuser.com", "origin");
   });
 
   asyncTest("checkEmailRegistration pending", function() {
@@ -377,7 +377,7 @@
   });
 
   asyncTest("checkEmailRegistration with XHR failure", function() {
-    failureCheck(network.checkEmailRegistration, "address");
+    failureCheck(network.checkEmailRegistration, "testuser@testuser.com");
   });
 
 
@@ -456,16 +456,15 @@
   });
 
 
-  asyncTest("requestPasswordReset", function() {
-    network.requestPasswordReset("address", "origin", function onSuccess() {
-      // XXX need a test here;
-      ok(true);
+  asyncTest("requestPasswordReset - true status", function() {
+    network.requestPasswordReset("testuser@testuser.com", "password", "origin", function onSuccess(status) {
+      equal(status, true, "password reset request success");
       start();
     }, testHelpers.unexpectedFailure);
   });
 
   asyncTest("requestPasswordReset with XHR failure", function() {
-    failureCheck(network.requestPasswordReset, "address", "origin");
+    failureCheck(network.requestPasswordReset, "testuser@testuser.com", "password", "origin");
   });
 
   asyncTest("setPassword happy case expects true status", function() {
diff --git a/resources/static/test/cases/shared/user.js b/resources/static/test/cases/shared/user.js
index 7fa7b2626..3489f27f4 100644
--- a/resources/static/test/cases/shared/user.js
+++ b/resources/static/test/cases/shared/user.js
@@ -490,24 +490,24 @@ var vep = require("./vep");
     );
   });
 
-  asyncTest("requestPasswordReset with known email", function() {
-    lib.requestPasswordReset("registered@testuser.com", function(status) {
+  asyncTest("requestPasswordReset with known email - true status", function() {
+    lib.requestPasswordReset("registered@testuser.com", "password", function(status) {
       equal(status.success, true, "password reset for known user");
       start();
     }, testHelpers.unexpectedXHRFailure);
   });
 
-  asyncTest("requestPasswordReset with unknown email", function() {
-    lib.requestPasswordReset("unregistered@testuser.com", function(status) {
+  asyncTest("requestPasswordReset with unknown email - false status, invalid_user", function() {
+    lib.requestPasswordReset("unregistered@testuser.com", "password", function(status) {
       equal(status.success, false, "password not reset for unknown user");
       equal(status.reason, "invalid_user", "invalid_user is the reason");
       start();
     }, testHelpers.unexpectedXHRFailure);
   });
 
-  asyncTest("requestPasswordReset with throttle", function() {
+  asyncTest("requestPasswordReset with throttle - false status, throttle", function() {
     xhr.useResult("throttle");
-    lib.requestPasswordReset("registered@testuser.com", function(status) {
+    lib.requestPasswordReset("registered@testuser.com", "password", function(status) {
       equal(status.success, false, "password not reset for throttle");
       equal(status.reason, "throttle", "password reset was throttled");
       start();
@@ -515,7 +515,7 @@ var vep = require("./vep");
   });
 
   asyncTest("requestPasswordReset with XHR failure", function() {
-    failureCheck(lib.requestPasswordReset, "registered@testuser.com");
+    failureCheck(lib.requestPasswordReset, "registered@testuser.com", "password");
   });
 
 
diff --git a/resources/static/test/testHelpers/helpers.js b/resources/static/test/testHelpers/helpers.js
index 31ac2d086..9c85bf656 100644
--- a/resources/static/test/testHelpers/helpers.js
+++ b/resources/static/test/testHelpers/helpers.js
@@ -29,7 +29,7 @@ BrowserID.TestHelpers = (function() {
       }
       calls[msg] = true;
 
-      cb && cb(msg, info);
+      cb && cb.apply(null, arguments);
     }));
   }
 
diff --git a/resources/views/test.ejs b/resources/views/test.ejs
index a6cf9e913..b7fcf298b 100644
--- a/resources/views/test.ejs
+++ b/resources/views/test.ejs
@@ -119,7 +119,6 @@
     <script src="/dialog/controllers/dialog.js"></script>
     <script src="/dialog/controllers/check_registration.js"></script>
     <script src="/dialog/controllers/authenticate.js"></script>
-    <script src="/dialog/controllers/forgot_password.js"></script>
     <script src="/dialog/controllers/required_email.js"></script>
     <script src="/dialog/controllers/verify_primary_user.js"></script>
     <script src="/dialog/controllers/generate_assertion.js"></script>
@@ -179,7 +178,6 @@
     <script src="cases/controllers/add_email.js"></script>
     <script src="cases/controllers/check_registration.js"></script>
     <script src="cases/controllers/authenticate.js"></script>
-    <script src="cases/controllers/forgot_password.js"></script>
     <script src="cases/controllers/required_email.js"></script>
     <script src="cases/controllers/verify_primary_user.js"></script>
     <script src="cases/controllers/generate_assertion.js"></script>
-- 
GitLab