diff --git a/browserid/static/dialog/resources/network.js b/browserid/static/dialog/resources/network.js index 18d04125647bf441df9d2ce293c4c617db293a25..f0fd530ddf3b84bc9e43ff9c545f5dec316dbebe 100644 --- a/browserid/static/dialog/resources/network.js +++ b/browserid/static/dialog/resources/network.js @@ -37,12 +37,12 @@ BrowserID.Network = (function() { "use strict"; - var csrf_token; - var xhr = $; - var server_time; - var auth_status; + var csrf_token, + xhr = $, + server_time, + auth_status; - function withContext(cb) { + function withContext(cb, error) { if (typeof auth_status === 'boolean' && typeof csrf_token !== 'undefined') cb(); else { xhr.ajax({ @@ -57,6 +57,7 @@ BrowserID.Network = (function() { auth_status = result.authenticated; _.defer(cb); }, + error: error, dataType: "json" }); } @@ -93,7 +94,7 @@ BrowserID.Network = (function() { success: options.success, error: options.error }); - }); + }, options.error); } var Network = { @@ -158,15 +159,16 @@ BrowserID.Network = (function() { } catch(e) { if (onFailure) onFailure(e.toString()); } - }); + }, onFailure); }, /** * Log the authenticated user out * @method logout * @param {function} [onSuccess] - called on completion + * @param {function} [onFailure] - Called on XHR failure. */ - logout: function(onSuccess) { + logout: function(onSuccess, onFailure) { post({ url: "/wsapi/logout", success: function() { @@ -177,7 +179,8 @@ BrowserID.Network = (function() { // user was successfully logged out. auth_status = false; if (onSuccess) _.defer(onSuccess); - } + }, + error: onFailure }); }, @@ -430,7 +433,7 @@ BrowserID.Network = (function() { _.delay(onSuccess, 0, status.success); } }, - failure: onFailure + error: onFailure }); }, @@ -482,7 +485,7 @@ BrowserID.Network = (function() { } catch(e) { onFailure(e.toString()); } - }); + }, onFailure); } }; 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 1f532b24b13568ae906990835a177272fa542cd6..840084d99456c32b871ec7862d82339dfdab6658 100644 --- a/browserid/static/dialog/test/qunit/resources/network_unit_test.js +++ b/browserid/static/dialog/test/qunit/resources/network_unit_test.js @@ -59,33 +59,54 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func }; + /** + * This is the results table, the keys are the request type, url, and + * a "selector" for testing. The right is the expected return value, already + * decoded. If a result is "undefined", the request's error handler will be + * called. + */ var xhr = { results: { - "get /wsapi/session_context valid": contextInfo, + "get /wsapi/session_context valid": contextInfo, "get /wsapi/session_context invalid": contextInfo, + // We are going to test for ajax errors for session_context using + // call to serverTime. We are going to use the flag contextAjaxError + "get /wsapi/session_context ajaxError": contextInfo, + "get /wsapi/session_context contextAjaxError": undefined, "post /wsapi/authenticate_user valid": { success: true }, "post /wsapi/authenticate_user invalid": { success: false }, + "post /wsapi/authenticate_user ajaxError": undefined, "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 valid": { success: true }, "post /wsapi/stage_user invalid": { success: false }, + "post /wsapi/stage_user ajaxError": undefined, "get /wsapi/user_creation_status?email=address notcreated": undefined, // undefined because server returns 400 error "get /wsapi/user_creation_status?email=address pending": { status: "pending" }, "get /wsapi/user_creation_status?email=address complete": { status: "complete" }, + "get /wsapi/user_creation_status?email=address ajaxError": undefined, "post /wsapi/complete_user_creation valid": { success: true }, "post /wsapi/complete_user_creation invalid": { success: false }, + "post /wsapi/complete_user_creation ajaxError": undefined, "post /wsapi/logout valid": { success: true }, + "post /wsapi/logout ajaxError": undefined, "get /wsapi/have_email?email=address taken": { email_known: true }, "get /wsapi/have_email?email=address nottaken" : { email_known: false }, + "get /wsapi/have_email?email=address ajaxError" : undefined, "post /wsapi/remove_email valid": { success: true }, "post /wsapi/remove_email invalid": { success: false }, + "post /wsapi/remove_email ajaxError": undefined, "post /wsapi/account_cancel valid": { success: true }, "post /wsapi/account_cancel invalid": { success: false }, + "post /wsapi/account_cancel ajaxError": undefined, "post /wsapi/stage_email valid": { success: true }, "post /wsapi/stage_email invalid": { success: false }, + "post /wsapi/stage_email ajaxError": undefined, "get /wsapi/email_addition_status?email=address notcreated": undefined, // undefined because server returns 400 error "get /wsapi/email_addition_status?email=address pending": { status: "pending" }, "get /wsapi/email_addition_status?email=address complete": { status: "complete" }, + "get /wsapi/email_addition_status?email=address ajaxError": undefined }, useResult: function(result) { @@ -165,6 +186,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func }); + wrappedAsyncTest("authenticate with ajax error after context already setup", function() { + xhr.useResult("ajaxError"); + network.authenticate("testuser@testuser.com", "ajaxError", function onSuccess(authenticated) { + ok(false, "ajax error should never pass"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should never pass"); + wrappedStart(); + }); + + stop(); + }); + + wrappedAsyncTest("checkAuth with valid authentication", function() { contextInfo.authenticated = true; network.checkAuth(function onSuccess(authenticated) { @@ -194,6 +229,22 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func }); + wrappedAsyncTest("checkAuth with ajax error", function() { + xhr.useResult("ajaxError"); + contextInfo.authenticated = false; + + network.checkAuth(function onSuccess() { + ok(true, "checkAuth does not make an ajax call, all good"); + wrappedStart(); + }, function onFailure() { + ok(false, "checkAuth does not make an ajax call, should not fail"); + wrappedStart(); + }); + + stop(); + }); + + wrappedAsyncTest("logout", function() { network.logout(function onSuccess() { ok(true, "we can logout"); @@ -207,6 +258,21 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func }); + wrappedAsyncTest("logout with ajax error", function() { + xhr.useResult("ajaxError"); + + network.logout(function onSuccess() { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); + + wrappedAsyncTest("complete_email_addition valid", function() { network.completeEmailRegistration("goodtoken", function onSuccess(proven) { equal(proven, true, "good token proved"); @@ -230,6 +296,19 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("complete_email_addition with ajax error", function() { + xhr.useResult("ajaxError"); + network.completeEmailRegistration("goodtoken", function onSuccess(proven) { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); + wrappedAsyncTest("createUser with valid user", function() { network.createUser("validuser", "origin", function onSuccess(created) { ok(created); @@ -253,6 +332,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("createUser with ajax error", function() { + xhr.useResult("ajaxError"); + + network.createUser("validuser", "origin", function onSuccess(created) { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); + wrappedAsyncTest("checkUserRegistration with pending email", function() { xhr.useResult("pending"); @@ -281,6 +374,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("checkUserRegistration with ajax error", function() { + xhr.useResult("ajaxError"); + + network.checkUserRegistration("address", function(status) { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); + wrappedAsyncTest("completeUserRegistration with valid token", function() { network.completeUserRegistration("token", "password", function(registered) { ok(registered); @@ -295,6 +402,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func wrappedAsyncTest("completeUserRegistration with invalid token", function() { xhr.useResult("invalid"); + network.completeUserRegistration("token", "password", function(registered) { equal(registered, false); wrappedStart(); @@ -306,7 +414,22 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("completeUserRegistration with ajax error", function() { + xhr.useResult("ajaxError"); + + network.completeUserRegistration("token", "password", function(registered) { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); + wrappedAsyncTest("cancelUser valid", function() { + network.cancelUser(function() { // XXX need a test here. ok(true); @@ -320,6 +443,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func wrappedAsyncTest("cancelUser invalid", function() { xhr.useResult("invalid"); + network.cancelUser(function() { // XXX need a test here. ok(true); @@ -331,6 +455,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("cancelUser with ajax error", function() { + xhr.useResult("ajaxError"); + + network.cancelUser(function() { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); + wrappedAsyncTest("emailRegistered with taken email", function() { xhr.useResult("taken"); @@ -359,6 +497,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("emailRegistered with ajax error", function() { + xhr.useResult("ajaxError"); + + network.emailRegistered("address", function(taken) { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); + wrappedAsyncTest("addEmail valid", function() { network.addEmail("address", "origin", function onSuccess(added) { @@ -385,6 +537,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("addEmail with ajax error", function() { + xhr.useResult("ajaxError"); + + network.addEmail("address", "origin", function onSuccess(added) { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); + wrappedAsyncTest("checkEmailRegistration pending", function() { xhr.useResult("pending"); @@ -413,6 +579,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("checkEmailRegistration with ajax error", function() { + xhr.useResult("ajaxError"); + + network.checkEmailRegistration("address", function(status) { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); + wrappedAsyncTest("removeEmail valid", function() { network.removeEmail("validemail", function onSuccess() { @@ -429,6 +609,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func wrappedAsyncTest("removeEmail invalid", function() { xhr.useResult("invalid"); + network.removeEmail("invalidemail", function onSuccess() { // XXX need a test here; ok(true); @@ -441,15 +622,18 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("removeEmail with ajax error", function() { + xhr.useResult("ajaxError"); - wrappedAsyncTest("setKey", function() { - ok(true, "setKey"); - start(); - }); + network.removeEmail("invalidemail", function onSuccess() { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); - wrappedAsyncTest("syncEmails", function() { - ok(true, "syncEmails"); - start(); + stop(); }); @@ -466,6 +650,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("requestPasswordReset with ajax error", function() { + xhr.useResult("ajaxError"); + + network.requestPasswordReset("address", "origin", function onSuccess() { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); + wrappedAsyncTest("resetPassword", function() { network.resetPassword("password", function onSuccess() { // XXX need a test here; @@ -479,6 +677,23 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("resetPassword with ajax error", function() { + xhr.useResult("ajaxError"); +/* + the body of this function is not yet written + + network.resetPassword("password", function onSuccess() { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + stop(); +*/ + start(); + }); + wrappedAsyncTest("changePassword", function() { network.changePassword("oldpassword", "newpassword", function onSuccess() { // XXX need a real wrappedAsyncTest here. @@ -492,6 +707,24 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + wrappedAsyncTest("changePassword with ajax error", function() { + xhr.useResult("ajaxError"); + + /* + the body of this function is not yet written. + network.changePassword("oldpassword", "newpassword", function onSuccess() { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + */ + start(); + }); + wrappedAsyncTest("serverTime", function() { // I am forcing the server time to be 1.25 seconds off. contextInfo.server_time = new Date().getTime() - 1250; @@ -509,4 +742,18 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/network", func stop(); }); + + wrappedAsyncTest("serverTime with ajax error before context has been setup", function() { + xhr.useResult("contextAjaxError"); + + network.serverTime(function onSuccess(time) { + ok(false, "ajax error should never call success"); + wrappedStart(); + }, function onFailure() { + ok(true, "ajax error should always call failure"); + wrappedStart(); + }); + + stop(); + }); });