diff --git a/browserid/static/dialog/controllers/dialog_controller.js b/browserid/static/dialog/controllers/dialog_controller.js index 1c7a39c8355acc4cefe382dafe0e670d66d3f686..6a16e5af9f9a6f76bbc9d23982b07304e60ca208 100644 --- a/browserid/static/dialog/controllers/dialog_controller.js +++ b/browserid/static/dialog/controllers/dialog_controller.js @@ -45,18 +45,28 @@ var bid = BrowserID, user = bid.User, errors = bid.Errors, - offline = false; + offline = false, + win = window; + PageController.extend("Dialog", {}, { - init: function(el) { + init: function(el, options) { + offline = false; + + options = options || {}; + + if(options.window) { + win = options.window; + } + var self=this; // keep track of where we are and what we do on success and error self.onsuccess = null; self.onerror = null; - setupChannel(self); + + win.setupChannel(self); self.stateMachine(); - }, getVerifiedEmail: function(origin_url, onsuccess, onerror) { @@ -74,7 +84,7 @@ self.doCheckAuth(); - $(window).bind("unload", function() { + $(win).bind("unload", function() { bid.Storage.setStagedOnBehalfOf(""); self.doCancel(); }); @@ -154,12 +164,16 @@ }, doOffline: function() { - this.renderError("wait.ejs", errors.offline); + this.renderError("offline.ejs", {}); offline = true; }, doXHRError: function(info) { - if (!offline) this.renderError("wait.ejs", errors.offline); + if (!offline) { + this.renderError("error.ejs", $.extend({ + action: errors.xhrError + }, info)); + } }, doConfirmUser: function(email) { diff --git a/browserid/static/dialog/controllers/page_controller.js b/browserid/static/dialog/controllers/page_controller.js index 01ddbb26a0671476402fb333de998bdb32998db5..37389fb1fa4690719e247d23131caecd67d84a7c 100644 --- a/browserid/static/dialog/controllers/page_controller.js +++ b/browserid/static/dialog/controllers/page_controller.js @@ -145,12 +145,16 @@ /** * Get a curried function to an error dialog. * @method getErrorDialog - * @method {object} info - info to use for the error dialog. Should have + * @method {object} action - info to use for the error dialog. Should have * two fields, message, description. */ - getErrorDialog: function(info) { + getErrorDialog: function(action) { var self=this; - return self.renderError.bind(self, "wait.ejs", info); + return function(lowLevelInfo) { + self.renderError("error.ejs", $.extend({ + action: action + }, lowLevelInfo)); + } }, onCancel: function(event) { diff --git a/browserid/static/dialog/dialog.js b/browserid/static/dialog/dialog.js index d3070184d61ce0bc8855bb5e552a55f8dbeae6f9..3c70f70d2a0ed2e7fc737f91fa9cd3fe3769c026 100644 --- a/browserid/static/dialog/dialog.js +++ b/browserid/static/dialog/dialog.js @@ -68,7 +68,9 @@ steal.plugins( .views('authenticate.ejs', 'confirmemail.ejs', 'pickemail.ejs', - 'wait.ejs' + 'wait.ejs', + 'error.ejs', + 'offline.ejs' ). then(function() { diff --git a/browserid/static/dialog/resources/error-messages.js b/browserid/static/dialog/resources/error-messages.js index 7064d82d9f4a1fdaaccd01341763679ab4a412e0..4235913d94eb4b963c3639153d9347a19f26fbc7 100644 --- a/browserid/static/dialog/resources/error-messages.js +++ b/browserid/static/dialog/resources/error-messages.js @@ -37,75 +37,68 @@ BrowserID.Errors = (function(){ var Errors = { authenticate: { - type: "serverError", title: "Error Authenticating", message: "There was a technical problem while trying to log you in. Yucky!" }, addEmail: { - type: "serverError", title: "Error Adding Address", message: "There was a technical problem while trying to add this email to your account. Yucky!" }, checkAuthentication: { - type: "serverError", title: "Error Checking Authentication", message: "There was a technical problem while trying to log you in. Yucky!" }, createUser: { - type: "serverError", title: "Error Creating Account", message: "There was a technical problem while trying to create your account. Yucky!" }, getAssertion: { - type: "serverError", title: "Error Getting Assertion", message: "There was a technical problem while trying to authenticate you. Yucky!" }, isEmailRegistered: { - type: "serverError", title: "Error Checking Email Address", message: "There was a technical problem while trying to check that email address. Yucky!" }, logoutUser: { - type: "serverError", title: "Logout Failed", message: "An error was encountered while signing you out. Yucky!" }, offline: { - type: "networkError", title: "You are offline!", message: "Unfortunately, BrowserID cannot communicate while offline!" }, registration: { - type: "serverError", title: "Registration Failed", message: "An error was encountered and the signup cannot be completed. Yucky!" }, requestPasswordReset: { - type: "serverError", title: "Error Resetting Password", message: "There was a technical problem while trying to reset your password." }, signIn: { - type: "serverError", title: "Signin Failed", message: "There was an error signing in. Yucky!" }, syncAddress: { - type: "serverError", title: "Error Syncing Address", message: "There was a technical problem while trying to synchronize your account. Yucky!" + }, + + xhrError: { + title: "Communication Error", + message: "It's embarrassing, but for some reason we cannot communicate with BrowserID at the moment. Please try again." } }; diff --git a/browserid/static/dialog/resources/network.js b/browserid/static/dialog/resources/network.js index 6f012b6e6e2d2a2c7b4fd9ab12e03af7ed449f76..dadbe3085fe7f2e7dd42f90f435c9cf6ffed7c0a 100644 --- a/browserid/static/dialog/resources/network.js +++ b/browserid/static/dialog/resources/network.js @@ -54,10 +54,10 @@ BrowserID.Network = (function() { } } - function xhrError(cb, errorMessage) { + function xhrError(cb, info) { return function() { - if (cb) cb(); - hub && hub.publish("xhrError", errorMessage); + if (cb) cb(info); + hub && hub.publish("xhrError", info); }; } @@ -69,7 +69,12 @@ BrowserID.Network = (function() { // that are thrown in the response handlers and it becomes very difficult // to debug. success: deferResponse(options.success), - error: deferResponse(xhrError(options.error, options.errorMessage)), + error: deferResponse(xhrError(options.error, { + network: { + type: "GET", + url: options.url + } + })), dataType: "json" }); } @@ -90,7 +95,13 @@ BrowserID.Network = (function() { // that are thrown in the response handlers and it becomes very difficult // to debug. success: deferResponse(options.success), - error: deferResponse(xhrError(options.error, options.errorMessage)) + error: deferResponse(xhrError(options.error, { + network: { + type: "POST", + url: options.url, + data: options.data + } + })) }); }, options.error); } @@ -98,6 +109,7 @@ BrowserID.Network = (function() { function withContext(cb, onFailure) { if (typeof auth_status === 'boolean' && typeof csrf_token !== 'undefined') cb(); else { + var url = "/wsapi/session_context"; xhr.ajax({ url: "/wsapi/session_context", success: function(result) { @@ -109,7 +121,12 @@ BrowserID.Network = (function() { auth_status = result.authenticated; cb(); }, - error: deferResponse(xhrError(onFailure)) + error: deferResponse(xhrError(onFailure, { + network: { + type: "GET", + url: url + } + })) }); } } diff --git a/browserid/static/dialog/test/qunit/controllers/dialog_controller_unit_test.js b/browserid/static/dialog/test/qunit/controllers/dialog_controller_unit_test.js new file mode 100644 index 0000000000000000000000000000000000000000..de0ce24219bcbf41bc3b679e3cc0924d90c6dc79 --- /dev/null +++ b/browserid/static/dialog/test/qunit/controllers/dialog_controller_unit_test.js @@ -0,0 +1,103 @@ +/*jshint browsers:true, forin: true, laxbreak: true */ +/*global steal: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID:true */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla BrowserID. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/controllers/dialog_controller", function() { + "use strict"; + + var controller, el + + function reset() { + el = $("#controller_head"); + el.find("#formWrap .contents").html(""); + el.find("#wait .contents").html(""); + el.find("#error .contents").html(""); + } + + module("controllers/dialog_controller", { + setup: function() { + reset(); + + controller = el.dialog({ + window: { + setupChannel: function() {} + } + }).controller(); + }, + + teardown: function() { + controller.destroy(); + reset(); + } + }); + + test("doOffline", function() { + controller.doOffline(); + ok($("#error .contents").length, "contents have been written"); + ok($("#error #offline").length, "offline error message has been written"); + }); + + test("doXHRError while online, no network info given", function() { + controller.doXHRError(); + ok($("#error .contents").length, "contents have been written"); + ok($("#error #action").length, "action contents have been written"); + equal($("#error #network").length, 0, "no network contents to be written"); + }); + + test("doXHRError while online, network info given", function() { + controller.doXHRError({ + network: { + type: "POST", + url: "browserid.org/verify" + } + }); + ok($("#error .contents").length, "contents have been written"); + ok($("#error #action").length, "action contents have been written"); + ok($("#error #network").length, "network contents have been written"); + }); + + test("doXHRError while offline does not update contents", function() { + controller.doOffline(); + $("#error #action").remove(); + + controller.doXHRError(); + ok(!$("#error #action").length, "XHR error is not reported if the user is offline."); + }); + + test("window.unload causes setStagedOnBehalfOf data to be cleared", function() { + }); + +}); + diff --git a/browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js b/browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js index e2df4dbb6a159fc4aa78c73b6a2e9c5842885996..a7bc08e0eee032725821584c1010917a7fb67809 100644 --- a/browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js +++ b/browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js @@ -48,7 +48,7 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", function() { el.find("#error .contents").html(""); } - module("PageController", { + module("/controllers/page_controller", { setup: function() { reset(); }, @@ -150,9 +150,10 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", function() { } }).controller(); + // This is the medium level info. var func = controller.getErrorDialog({ - title: "error title", - message: "error message" + title: "medium level info error title", + message: "medium level info error message" }); equal(typeof func, "function", "a function was returned from getErrorDialog"); diff --git a/browserid/static/dialog/test/qunit/qunit.js b/browserid/static/dialog/test/qunit/qunit.js index 7ceaabc35ed72539a42d52a9a1bab5412d3525d3..ffecc86f2b4e903e5884105bdf51827953fa2a96 100644 --- a/browserid/static/dialog/test/qunit/qunit.js +++ b/browserid/static/dialog/test/qunit/qunit.js @@ -1,5 +1,6 @@ steal("/dialog/resources/browserid.js", "/dialog/resources/browser-support.js", + "/dialog/resources/error-messages.js", "/dialog/resources/storage.js", "/dialog/resources/tooltip.js", "/dialog/resources/validation.js", @@ -11,9 +12,11 @@ steal("/dialog/resources/browserid.js", "jquery/controller/view", "jquery/view/ejs", "funcunit/qunit") - .views('testBodyTemplate.ejs') - .views('wait.ejs', - 'pickemail.ejs') + .views('testBodyTemplate.ejs', + 'wait.ejs', + 'pickemail.ejs', + 'offline.ejs', + 'error.ejs') .then("browserid_unit_test") .then("include_unit_test") .then("relay/relay_unit_test") @@ -26,4 +29,5 @@ steal("/dialog/resources/browserid.js", .then("resources/user_unit_test") .then("controllers/page_controller_unit_test") .then("controllers/pickemail_controller_unit_test") + .then("controllers/dialog_controller_unit_test") diff --git a/browserid/static/dialog/test/qunit/resources/network_unit_test.js b/browserid/static/dialog/test/qunit/resources/network_unit_test.js index ac5b06c785ffba87efe2ea7f3d2b3404aefb6b16..74d35d40f086fc6a0bf30d66cc332b4799361237 100644 --- a/browserid/static/dialog/test/qunit/resources/network_unit_test.js +++ b/browserid/static/dialog/test/qunit/resources/network_unit_test.js @@ -61,8 +61,10 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func var handle; - var subscriber = function() { + var subscriber = function(message, info) { ok(true, "xhr error notified application"); + ok(info.network.url, "url is in network info"); + ok(info.network.type, "request type is in network info"); wrappedStart(); OpenAjax.hub.unsubscribe(handle); }; @@ -85,8 +87,10 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func args.push(function onSuccess(authenticated) { ok(false, "XHR failure should never pass"); wrappedStart(); - }, function onFailure() { + }, 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"); wrappedStart(); }); @@ -195,7 +199,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func } - module("network", { + module("/resources/network", { setup: function() { network.setXHR(xhr); xhr.useResult("valid"); diff --git a/browserid/static/dialog/views/error.ejs b/browserid/static/dialog/views/error.ejs new file mode 100644 index 0000000000000000000000000000000000000000..336fc1d4c442bdea03251b4a8e6f707434ba5764 --- /dev/null +++ b/browserid/static/dialog/views/error.ejs @@ -0,0 +1,27 @@ + + <h2>There has been an error!</h2> + + <p> + We are very sorry, but there has been an error. You can open the error message below + if you care to find out more information. To retry, you will have to close BrowserID and try again. + </p> + + <a href="#">See more info</a> + + <aside> + <% if (typeof action !== "undefined") { %> + <h3 id="action">Action: <%= action.title %></h3> + <p> + <%= action.message %> + </p> + <% } %> + + <% if (typeof network !== "undefined") { %> + <h3 id="network">Network Info: </h3> + <p> + <%= network.type %>: <%= network.url %> + </p> + + <% } %> + + </aside> diff --git a/browserid/static/dialog/views/offline.ejs b/browserid/static/dialog/views/offline.ejs new file mode 100644 index 0000000000000000000000000000000000000000..677951bef1fc6a0fb4cb93eab91ed11c48f7d452 --- /dev/null +++ b/browserid/static/dialog/views/offline.ejs @@ -0,0 +1,8 @@ + + <h2 id="offline">You are offline!</h2> + + <p> + We are sorry, but we cannot communicate with BrowserID while you are offline. + </p> + +