Newer
Older
/*jshint browser: true laxbreak: true, expr: true */
/*global BrowserID: true, ok: true, equal: true, start: true, deepEqual: true, notEqual: true */
Shane Tomlinson
committed
/* 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/. */
Shane Tomlinson
committed
BrowserID.TestHelpers = (function() {
"use strict";
var bid = BrowserID,
mediator = bid.Mediator,
network = bid.Network,
storage = bid.Storage,
xhr = bid.XHR,
transport = bid.Mocks.xhr,
provisioning = bid.Mocks.Provisioning,
screens = bid.Screens,
Shane Tomlinson
committed
registrations = [],
Lloyd Hilaiel
committed
testOrigin = "https://login.persona.org";
function register(message, cb) {
registrations.push(mediator.subscribe(message, function(msg, info) {
if(calls[msg]) {
throw msg + " triggered more than once";
}
calls[msg] = info || true;
cb && cb.apply(null, arguments);
}));
}
function unregisterAll() {
for(var i = 0, registration; registration = registrations[i]; ++i) {
mediator.unsubscribe(registration);
}
registrations = [];
calls = {};
}
function checkNetworkError() {
ok($("#error .contents").text().length, "contents have been written");
ok($("#error #action").text().length, "action contents have been written");
ok($("#error #network").text().length, "network contents have been written");
}
function clearStorage() {
for(var key in localStorage) {
localStorage.removeItem(key);
}
}
Shane Tomlinson
committed
var TestHelpers = {
XHR_TIME_UNTIL_DELAY: 100,
setup: function() {
unregisterAll();
mediator.reset();
xhr.init({
transport: transport,
Shane Tomlinson
committed
time_until_delay: TestHelpers.XHR_TIME_UNTIL_DELAY
});
transport.setDelay(0);
transport.setContextInfo("auth_level", undefined);
transport.setContextInfo("cookies_enabled", true);
transport.useResult("valid");
Shane Tomlinson
committed
network.init({ forceCookieStatus: undefined });
Shane Tomlinson
committed
$("body").stop().show();
$("body")[0].className = "";
$(".error").removeClass("error");
$("#error").hide();
$(".notification").stop().hide();
screens.wait.hide();
screens.error.hide();
screens.delay.hide();
provisioning.setStatus(provisioning.NOT_AUTHENTICATED);
Shane Tomlinson
committed
user.reset();
user.init({
provisioning: provisioning
});
user.setOrigin(testOrigin);
Shane Tomlinson
committed
},
teardown: function() {
unregisterAll();
mediator.reset();
xhr.init({
transport: $,
Shane Tomlinson
committed
time_until_delay: 10 * 1000
});
network.init();
screens.wait.hide();
screens.error.hide();
screens.delay.hide();
provisioning.setStatus(provisioning.NOT_AUTHENTICATED);
testOrigin: testOrigin,
register: register,
Shane Tomlinson
committed
isTriggered: function(message) {
return message in calls;
Shane Tomlinson
committed
},
Shane Tomlinson
committed
testTriggered: function(message, expectedFields) {
ok(message in calls, message + " was triggered");
if (expectedFields) this.testObjectValuesEqual(calls[message], expectedFields);
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
expectedMessage: function(message, expectedFields) {
// keep track of the original start function. When the start function is
// called, call the proxy start function and then the original start
// function. This allows proxy start functions to be chained and multiple
// expectedMessages to be called.
start = function(origStart) {
TestHelpers.testTriggered(message, expectedFields);
start = origStart;
start();
}.bind(null, start);
register(message);
},
unexpectedMessage: function(message) {
// keep track of the original start function. When the start function is
// called, call the proxy start function and then the original start
// function. This allows proxy start functions to be chained and multiple
// expectedMessages to be called.
start = function(origStart) {
equal(TestHelpers.isTriggered(message), false, message + " was not triggered");
start = origStart;
start();
}.bind(null, start);
register(message);
},
errorVisible: function() {
return screens.error.visible;
},
Shane Tomlinson
committed
testErrorVisible: function() {
Shane Tomlinson
committed
equal(TestHelpers.errorVisible(), true, "error screen is visible");
Shane Tomlinson
committed
Shane Tomlinson
committed
testErrorNotVisible: function() {
equal(TestHelpers.errorVisible(), false, "error screen is not visible");
},
waitVisible: function() {
return screens.wait.visible;
},
testWaitVisible: function() {
equal(TestHelpers.waitVisible(), true, "wait screen is visible");
},
Shane Tomlinson
committed
delayVisible: function() {
return screens.delay.visible;
},
testDelayVisible: function() {
equal(TestHelpers.delayVisible(), true, "delay screen is visible");
},
checkNetworkError: checkNetworkError,
unexpectedSuccess: function() {
ok(false, "unexpected success");
start();
},
expectedXHRFailure: function() {
ok(true, "expected XHR failure");
start();
},
unexpectedXHRFailure: function() {
ok(false, "unexpected XHR failure");
start();
},
testTooltipVisible: function() {
equal(tooltip.shown, true, "tooltip is visible");
testTooltipNotVisible: function() {
equal(tooltip.shown, false, "tooltip is not visible");
},
failureCheck: 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 = [].slice.call(arguments, 1);
var errorInfo;
args.push(bid.TestHelpers.unexpectedSuccess, 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");
start();
});
Shane Tomlinson
committed
if(transport.responseName === "valid") {
transport.useResult("ajaxError");
Shane Tomlinson
committed
},
/**
* Generate a long string
*/
generateString: function(length) {
var str = "";
for(var i = 0; i < length; i++) {
str += (i % 10);
}
return str;
Shane Tomlinson
committed
},
testKeysInObject: function(objToTest, expected, msg) {
if (!objToTest) ok(false, "missing objToTest");
if (!expected) ok(false, "missing objToTest");
Shane Tomlinson
committed
Shane Tomlinson
committed
for(var i=0, key; key=expected[i]; ++i) {
ok(key in objToTest, msg || ("object contains " + key));
Shane Tomlinson
committed
}
Shane Tomlinson
committed
testObjectValuesEqual: function(objToTest, expected, msg) {
if (!objToTest) ok(false, "missing objToTest");
if (!expected) ok(false, "missing objToTest");
Shane Tomlinson
committed
for(var key in expected) {
deepEqual(objToTest[key], expected[key], key + " set to: " + expected[key] + (msg ? " - " + msg : ""));
Shane Tomlinson
committed
}
},
Shane Tomlinson
committed
testUndefined: function(toTest, msg) {
equal(typeof toTest, "undefined", msg || "object is undefined");
},
testNotUndefined: function(toTest, msg) {
notEqual(typeof toTest, "undefined", msg || "object is defined");
testVisible: function(selector, msg) {
ok($(selector).is(":visible"), msg || selector + " is visible");
},
testNotVisible: function(selector, msg) {
equal($(selector).is(":visible"), false, msg || selector + " is not visible");
Shane Tomlinson
committed
},
Shane Tomlinson
committed
testHasClass: function(selector, className, msg) {
ok($(selector).hasClass(className),
msg || (selector + " has className " + className));
},
testNotHasClass: function(selector, className, msg) {
ok(!$(selector).hasClass(className),
msg || (selector + " does not have className " + className));
},
testElementExists: function(selector, msg) {
ok($(selector).length, msg || ("element '" + selector + "' exists"));
Shane Tomlinson
committed
Shane Tomlinson
committed
testElementDoesNotExist: function(selector, msg) {
ok(!$(selector).length, msg || ("element '" + selector + "' does not exist"));
Shane Tomlinson
committed
},
testRPTosPPShown: function(msg) {
TestHelpers.testHasClass("body", "rptospp", msg || "RP TOS/PP shown");
},
testRPTosPPNotShown: function(msg) {
TestHelpers.testNotHasClass("body", "rptospp", msg || "RP TOS/PP not shown");
},
testElementChecked: function(selector, msg) {
equal($(selector).is(":checked"), true, msg || selector + " is checked");
},
testElementNotChecked: function(selector, msg) {
equal($(selector).is(":checked"), false, msg || selector + " is not checked");
},
testElementFocused: function(selector, msg) {
var focusedEl = $(":focus");
if ($(selector).is(":focus")) {
ok(true, msg || selector + " is focused");
}
else {
// In some environments such as PhantomJS, input elements cannot be
// checked for focus. Make a temporary input element which we can
// check to see if it is possible to focus. If it is possible, this is
// a failure. If it is not possible, print a message and continue.
// Remove the element when complete.
var input = $("<input type='radio' />").appendTo("body").focus();
if (input.is(":focus")) {
ok(false, msg || selector + " is focused");
// refocus the original input element.
if (focusedEl.length) $(focusedEl).focus();
}
else {
window.console && console.log("currently unable to focus elements, focus check skipped - try focusing the unit test page");
}
input.remove();
}
},
Shane Tomlinson
committed
testEmailMarkedVerified: function(email, msg) {
var emailInfo = storage.getEmail(email);
equal(emailInfo && emailInfo.verified, true,
"verified bit set for " + email);
},
testDocumentRedirected: function(doc, expectedHref, msg) {
equal(doc.location, expectedHref, msg || "document redirected to " + expectedHref);
},
testDocumentNotRedirected: function(doc, msg) {
equal(doc.location.href, document.location.href, msg || "document not redirected");
Shane Tomlinson
committed
return TestHelpers;