1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/modules/test/browser_SignInToWebsite.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,566 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + * http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +"use strict"; 1.8 + 1.9 +/** 1.10 + * TO TEST: 1.11 + * - test state saved on doorhanger dismissal 1.12 + * - links to switch steps 1.13 + * - TOS and PP link clicks 1.14 + * - identityList is populated correctly 1.15 + */ 1.16 + 1.17 +Services.prefs.setBoolPref("toolkit.identity.debug", true); 1.18 + 1.19 +XPCOMUtils.defineLazyModuleGetter(this, "IdentityService", 1.20 + "resource://gre/modules/identity/Identity.jsm"); 1.21 + 1.22 +const TEST_ORIGIN = "https://example.com"; 1.23 +const TEST_EMAIL = "user@example.com"; 1.24 + 1.25 +let gTestIndex = 0; 1.26 +let outerWinId = gBrowser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.27 + .getInterface(Ci.nsIDOMWindowUtils).outerWindowID; 1.28 + 1.29 +function NotificationBase(aNotId) { 1.30 + this.id = aNotId; 1.31 +} 1.32 +NotificationBase.prototype = { 1.33 + message: TEST_ORIGIN, 1.34 + mainAction: { 1.35 + label: "", 1.36 + callback: function() { 1.37 + this.mainActionClicked = true; 1.38 + }.bind(this), 1.39 + }, 1.40 + secondaryActions: [], 1.41 + options: { 1.42 + "identity": { 1.43 + origin: TEST_ORIGIN, 1.44 + rpId: outerWinId, 1.45 + }, 1.46 + }, 1.47 +}; 1.48 + 1.49 +let tests = [ 1.50 + { 1.51 + name: "test_request_required_typed", 1.52 + 1.53 + run: function() { 1.54 + setupRPFlow(); 1.55 + this.notifyOptions = { 1.56 + rpId: outerWinId, 1.57 + origin: TEST_ORIGIN, 1.58 + }; 1.59 + this.notifyObj = new NotificationBase("identity-request"); 1.60 + Services.obs.notifyObservers({wrappedJSObject: this.notifyOptions}, 1.61 + "identity-request", null); 1.62 + }, 1.63 + 1.64 + onShown: function(popup) { 1.65 + checkPopup(popup, this.notifyObj); 1.66 + let notification = popup.childNodes[0]; 1.67 + 1.68 + // Check identity popup state 1.69 + let state = notification.identity; 1.70 + ok(!state.typedEmail, "Nothing should be typed yet"); 1.71 + ok(!state.selected, "Identity should not be selected yet"); 1.72 + ok(!state.termsOfService, "No TOS specified"); 1.73 + ok(!state.privacyPolicy, "No PP specified"); 1.74 + is(state.step, 0, "Step should be persisted with default value"); 1.75 + is(state.rpId, outerWinId, "Check rpId"); 1.76 + is(state.origin, TEST_ORIGIN, "Check origin"); 1.77 + 1.78 + is(notification.step, 0, "Should be on the new email step"); 1.79 + is(notification.chooseEmailLink.hidden, true, "Identity list is empty so link to list view should be hidden"); 1.80 + is(notification.addEmailLink.parentElement.hidden, true, "We are already on the email input step so choose email pane should be hidden"); 1.81 + is(notification.emailField.value, "", "Email field should default to empty on a new notification"); 1.82 + let notifDoc = notification.ownerDocument; 1.83 + ok(notifDoc.getAnonymousElementByAttribute(notification, "anonid", "tos").hidden, 1.84 + "TOS link should be hidden"); 1.85 + ok(notifDoc.getAnonymousElementByAttribute(notification, "anonid", "privacypolicy").hidden, 1.86 + "PP link should be hidden"); 1.87 + 1.88 + // Try to continue with a missing email address 1.89 + triggerMainCommand(popup); 1.90 + is(notification.throbber.style.visibility, "hidden", "is throbber visible"); 1.91 + ok(!notification.button.disabled, "Button should not be disabled"); 1.92 + is(window.gIdentitySelected, null, "Check no identity selected"); 1.93 + 1.94 + // Fill in an invalid email address and try again 1.95 + notification.emailField.value = "foo"; 1.96 + triggerMainCommand(popup); 1.97 + is(notification.throbber.style.visibility, "hidden", "is throbber visible"); 1.98 + ok(!notification.button.disabled, "Button should not be disabled"); 1.99 + is(window.gIdentitySelected, null, "Check no identity selected"); 1.100 + 1.101 + // Fill in an email address and try again 1.102 + notification.emailField.value = TEST_EMAIL; 1.103 + triggerMainCommand(popup); 1.104 + is(window.gIdentitySelected.rpId, outerWinId, "Check identity selected rpId"); 1.105 + is(window.gIdentitySelected.identity, TEST_EMAIL, "Check identity selected email"); 1.106 + is(notification.identity.selected, TEST_EMAIL, "Check persisted email"); 1.107 + is(notification.throbber.style.visibility, "visible", "is throbber visible"); 1.108 + ok(notification.button.disabled, "Button should be disabled"); 1.109 + ok(notification.emailField.disabled, "Email field should be disabled"); 1.110 + ok(notification.identityList.disabled, "Identity list should be disabled"); 1.111 + 1.112 + PopupNotifications.getNotification("identity-request").remove(); 1.113 + }, 1.114 + 1.115 + onHidden: function(popup) { }, 1.116 + }, 1.117 + { 1.118 + name: "test_request_optional", 1.119 + 1.120 + run: function() { 1.121 + this.notifyOptions = { 1.122 + rpId: outerWinId, 1.123 + origin: TEST_ORIGIN, 1.124 + privacyPolicy: TEST_ORIGIN + "/pp.txt", 1.125 + termsOfService: TEST_ORIGIN + "/tos.tzt", 1.126 + }; 1.127 + this.notifyObj = new NotificationBase("identity-request"); 1.128 + Services.obs.notifyObservers({ wrappedJSObject: this.notifyOptions }, 1.129 + "identity-request", null); 1.130 + }, 1.131 + 1.132 + onShown: function(popup) { 1.133 + checkPopup(popup, this.notifyObj); 1.134 + let notification = popup.childNodes[0]; 1.135 + 1.136 + // Check identity popup state 1.137 + let state = notification.identity; 1.138 + ok(!state.typedEmail, "Nothing should be typed yet"); 1.139 + ok(!state.selected, "Identity should not be selected yet"); 1.140 + is(state.termsOfService, this.notifyOptions.termsOfService, "Check TOS URL"); 1.141 + is(state.privacyPolicy, this.notifyOptions.privacyPolicy, "Check PP URL"); 1.142 + is(state.step, 0, "Step should be persisted with default value"); 1.143 + is(state.rpId, outerWinId, "Check rpId"); 1.144 + is(state.origin, TEST_ORIGIN, "Check origin"); 1.145 + 1.146 + is(notification.step, 0, "Should be on the new email step"); 1.147 + is(notification.chooseEmailLink.hidden, true, "Identity list is empty so link to list view should be hidden"); 1.148 + is(notification.addEmailLink.parentElement.hidden, true, "We are already on the email input step so choose email pane should be hidden"); 1.149 + is(notification.emailField.value, "", "Email field should default to empty on a new notification"); 1.150 + let notifDoc = notification.ownerDocument; 1.151 + let tosLink = notifDoc.getAnonymousElementByAttribute(notification, "anonid", "tos"); 1.152 + ok(!tosLink.hidden, "TOS link should be visible"); 1.153 + is(tosLink.href, this.notifyOptions.termsOfService, "Check TOS link URL"); 1.154 + let ppLink = notifDoc.getAnonymousElementByAttribute(notification, "anonid", "privacypolicy"); 1.155 + ok(!ppLink.hidden, "PP link should be visible"); 1.156 + is(ppLink.href, this.notifyOptions.privacyPolicy, "Check PP link URL"); 1.157 + 1.158 + // Try to continue with a missing email address 1.159 + triggerMainCommand(popup); 1.160 + is(notification.throbber.style.visibility, "hidden", "is throbber visible"); 1.161 + ok(!notification.button.disabled, "Button should not be disabled"); 1.162 + is(window.gIdentitySelected, null, "Check no identity selected"); 1.163 + 1.164 + // Fill in an invalid email address and try again 1.165 + notification.emailField.value = "foo"; 1.166 + triggerMainCommand(popup); 1.167 + is(notification.throbber.style.visibility, "hidden", "is throbber visible"); 1.168 + ok(!notification.button.disabled, "Button should not be disabled"); 1.169 + is(window.gIdentitySelected, null, "Check no identity selected"); 1.170 + 1.171 + // Fill in an email address and try again 1.172 + notification.emailField.value = TEST_EMAIL; 1.173 + triggerMainCommand(popup); 1.174 + is(window.gIdentitySelected.rpId, outerWinId, "Check identity selected rpId"); 1.175 + is(window.gIdentitySelected.identity, TEST_EMAIL, "Check identity selected email"); 1.176 + is(notification.identity.selected, TEST_EMAIL, "Check persisted email"); 1.177 + is(notification.throbber.style.visibility, "visible", "is throbber visible"); 1.178 + ok(notification.button.disabled, "Button should be disabled"); 1.179 + ok(notification.emailField.disabled, "Email field should be disabled"); 1.180 + ok(notification.identityList.disabled, "Identity list should be disabled"); 1.181 + 1.182 + PopupNotifications.getNotification("identity-request").remove(); 1.183 + }, 1.184 + 1.185 + onHidden: function(popup) {}, 1.186 + }, 1.187 + { 1.188 + name: "test_login_state_changed", 1.189 + run: function () { 1.190 + this.notifyOptions = { 1.191 + rpId: outerWinId, 1.192 + }; 1.193 + this.notifyObj = new NotificationBase("identity-logged-in"); 1.194 + this.notifyObj.message = "Signed in as: user@example.com"; 1.195 + this.notifyObj.mainAction.label = "Sign Out"; 1.196 + this.notifyObj.mainAction.accessKey = "O"; 1.197 + Services.obs.notifyObservers({ wrappedJSObject: this.notifyOptions }, 1.198 + "identity-login-state-changed", TEST_EMAIL); 1.199 + executeSoon(function() { 1.200 + PopupNotifications.getNotification("identity-logged-in").anchorElement.click(); 1.201 + }); 1.202 + }, 1.203 + 1.204 + onShown: function(popup) { 1.205 + checkPopup(popup, this.notifyObj); 1.206 + 1.207 + // Fire the notification that the user is no longer logged-in to close the UI. 1.208 + Services.obs.notifyObservers({ wrappedJSObject: this.notifyOptions }, 1.209 + "identity-login-state-changed", null); 1.210 + }, 1.211 + 1.212 + onHidden: function(popup) {}, 1.213 + }, 1.214 + { 1.215 + name: "test_login_state_changed_logout", 1.216 + run: function () { 1.217 + this.notifyOptions = { 1.218 + rpId: outerWinId, 1.219 + }; 1.220 + this.notifyObj = new NotificationBase("identity-logged-in"); 1.221 + this.notifyObj.message = "Signed in as: user@example.com"; 1.222 + this.notifyObj.mainAction.label = "Sign Out"; 1.223 + this.notifyObj.mainAction.accessKey = "O"; 1.224 + Services.obs.notifyObservers({ wrappedJSObject: this.notifyOptions }, 1.225 + "identity-login-state-changed", TEST_EMAIL); 1.226 + executeSoon(function() { 1.227 + PopupNotifications.getNotification("identity-logged-in").anchorElement.click(); 1.228 + }); 1.229 + }, 1.230 + 1.231 + onShown: function(popup) { 1.232 + checkPopup(popup, this.notifyObj); 1.233 + 1.234 + // This time trigger the Sign Out button and make sure the UI goes away. 1.235 + triggerMainCommand(popup); 1.236 + }, 1.237 + 1.238 + onHidden: function(popup) {}, 1.239 + }, 1.240 +]; 1.241 + 1.242 +function test_auth() { 1.243 + let notifyOptions = { 1.244 + provId: outerWinId, 1.245 + origin: TEST_ORIGIN, 1.246 + }; 1.247 + 1.248 + Services.obs.addObserver(function() { 1.249 + // prepare to send auth-complete and close the window 1.250 + let winCloseObs = new WindowObserver(function(closedWin) { 1.251 + info("closed window"); 1.252 + finish(); 1.253 + }, "domwindowclosed"); 1.254 + Services.ww.registerNotification(winCloseObs); 1.255 + Services.obs.notifyObservers(null, "identity-auth-complete", IdentityService.IDP.authenticationFlowSet.authId); 1.256 + 1.257 + }, "test-identity-auth-window", false); 1.258 + 1.259 + let winObs = new WindowObserver(function(authWin) { 1.260 + ok(authWin, "Authentication window opened"); 1.261 + ok(authWin.contentWindow.location); 1.262 + }); 1.263 + 1.264 + Services.ww.registerNotification(winObs); 1.265 + 1.266 + Services.obs.notifyObservers({ wrappedJSObject: notifyOptions }, 1.267 + "identity-auth", TEST_ORIGIN + "/auth"); 1.268 +} 1.269 + 1.270 +function test() { 1.271 + waitForExplicitFinish(); 1.272 + 1.273 + let sitw = {}; 1.274 + try { 1.275 + Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw); 1.276 + } catch (ex) { 1.277 + ok(true, "Skip the test since SignInToWebsite.jsm isn't packaged outside outside mozilla-central"); 1.278 + finish(); 1.279 + return; 1.280 + } 1.281 + 1.282 + PopupNotifications.transitionsEnabled = false; 1.283 + 1.284 + registerCleanupFunction(cleanUp); 1.285 + 1.286 + ok(sitw.SignInToWebsiteUX, "SignInToWebsiteUX object exists"); 1.287 + if (!Services.prefs.getBoolPref("dom.identity.enabled")) { 1.288 + // If the pref isn't enabled then init wasn't called so do that for the test. 1.289 + sitw.SignInToWebsiteUX.init(); 1.290 + } 1.291 + 1.292 + // Replace implementation of ID Service functions for testing 1.293 + window.selectIdentity = sitw.SignInToWebsiteUX.selectIdentity; 1.294 + sitw.SignInToWebsiteUX.selectIdentity = function(aRpId, aIdentity) { 1.295 + info("Identity selected: " + aIdentity); 1.296 + window.gIdentitySelected = {rpId: aRpId, identity: aIdentity}; 1.297 + }; 1.298 + 1.299 + window.setAuthenticationFlow = IdentityService.IDP.setAuthenticationFlow; 1.300 + IdentityService.IDP.setAuthenticationFlow = function(aAuthId, aProvId) { 1.301 + info("setAuthenticationFlow: " + aAuthId + " : " + aProvId); 1.302 + this.authenticationFlowSet = { authId: aAuthId, provId: aProvId }; 1.303 + Services.obs.notifyObservers(null, "test-identity-auth-window", aAuthId); 1.304 + }; 1.305 + 1.306 + runNextTest(); 1.307 +} 1.308 + 1.309 +// Cleanup between tests 1.310 +function resetState() { 1.311 + delete window.gIdentitySelected; 1.312 + delete IdentityService.IDP.authenticationFlowSet; 1.313 + IdentityService.reset(); 1.314 +} 1.315 + 1.316 +// Cleanup after all tests 1.317 +function cleanUp() { 1.318 + info("cleanup"); 1.319 + resetState(); 1.320 + 1.321 + PopupNotifications.transitionsEnabled = true; 1.322 + 1.323 + for (let topic in gActiveObservers) 1.324 + Services.obs.removeObserver(gActiveObservers[topic], topic); 1.325 + for (let eventName in gActiveListeners) 1.326 + PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false); 1.327 + delete IdentityService.RP._rpFlows[outerWinId]; 1.328 + 1.329 + // Put the JSM functions back to how they were 1.330 + IdentityService.IDP.setAuthenticationFlow = window.setAuthenticationFlow; 1.331 + delete window.setAuthenticationFlow; 1.332 + 1.333 + let sitw = {}; 1.334 + Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw); 1.335 + sitw.SignInToWebsiteUX.selectIdentity = window.selectIdentity; 1.336 + delete window.selectIdentity; 1.337 + if (!Services.prefs.getBoolPref("dom.identity.enabled")) { 1.338 + sitw.SignInToWebsiteUX.uninit(); 1.339 + } 1.340 + 1.341 + Services.prefs.clearUserPref("toolkit.identity.debug"); 1.342 +} 1.343 + 1.344 +let gActiveListeners = {}; 1.345 +let gActiveObservers = {}; 1.346 +let gShownState = {}; 1.347 + 1.348 +function runNextTest() { 1.349 + let nextTest = tests[gTestIndex]; 1.350 + 1.351 + function goNext() { 1.352 + resetState(); 1.353 + if (++gTestIndex == tests.length) 1.354 + executeSoon(test_auth); 1.355 + else 1.356 + executeSoon(runNextTest); 1.357 + } 1.358 + 1.359 + function addObserver(topic) { 1.360 + function observer() { 1.361 + Services.obs.removeObserver(observer, "PopupNotifications-" + topic); 1.362 + delete gActiveObservers["PopupNotifications-" + topic]; 1.363 + 1.364 + info("[Test #" + gTestIndex + "] observer for " + topic + " called"); 1.365 + nextTest[topic](); 1.366 + goNext(); 1.367 + } 1.368 + Services.obs.addObserver(observer, "PopupNotifications-" + topic, false); 1.369 + gActiveObservers["PopupNotifications-" + topic] = observer; 1.370 + } 1.371 + 1.372 + if (nextTest.backgroundShow) { 1.373 + addObserver("backgroundShow"); 1.374 + } else if (nextTest.updateNotShowing) { 1.375 + addObserver("updateNotShowing"); 1.376 + } else { 1.377 + doOnPopupEvent("popupshowing", function () { 1.378 + info("[Test #" + gTestIndex + "] popup showing"); 1.379 + }); 1.380 + doOnPopupEvent("popupshown", function () { 1.381 + gShownState[gTestIndex] = true; 1.382 + info("[Test #" + gTestIndex + "] popup shown"); 1.383 + nextTest.onShown(this); 1.384 + }); 1.385 + 1.386 + // We allow multiple onHidden functions to be defined in an array. They're 1.387 + // called in the order they appear. 1.388 + let onHiddenArray = nextTest.onHidden instanceof Array ? 1.389 + nextTest.onHidden : 1.390 + [nextTest.onHidden]; 1.391 + doOnPopupEvent("popuphidden", function () { 1.392 + if (!gShownState[gTestIndex]) { 1.393 + // TODO: needed? 1.394 + info("Popup from test " + gTestIndex + " was hidden before its popupshown fired"); 1.395 + } 1.396 + 1.397 + let onHidden = onHiddenArray.shift(); 1.398 + info("[Test #" + gTestIndex + "] popup hidden (" + onHiddenArray.length + " hides remaining)"); 1.399 + executeSoon(function () { 1.400 + onHidden.call(nextTest, this); 1.401 + if (!onHiddenArray.length) 1.402 + goNext(); 1.403 + }.bind(this)); 1.404 + }, onHiddenArray.length); 1.405 + info("[Test #" + gTestIndex + "] added listeners; panel state: " + PopupNotifications.isPanelOpen); 1.406 + } 1.407 + 1.408 + info("[Test #" + gTestIndex + "] running test"); 1.409 + nextTest.run(); 1.410 +} 1.411 + 1.412 +function doOnPopupEvent(eventName, callback, numExpected) { 1.413 + gActiveListeners[eventName] = function (event) { 1.414 + if (event.target != PopupNotifications.panel) 1.415 + return; 1.416 + if (typeof(numExpected) === "number") 1.417 + numExpected--; 1.418 + if (!numExpected) { 1.419 + PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false); 1.420 + delete gActiveListeners[eventName]; 1.421 + } 1.422 + 1.423 + callback.call(PopupNotifications.panel); 1.424 + }; 1.425 + PopupNotifications.panel.addEventListener(eventName, gActiveListeners[eventName], false); 1.426 +} 1.427 + 1.428 +function checkPopup(popup, notificationObj) { 1.429 + info("[Test #" + gTestIndex + "] checking popup"); 1.430 + 1.431 + let notifications = popup.childNodes; 1.432 + is(notifications.length, 1, "only one notification displayed"); 1.433 + let notification = notifications[0]; 1.434 + let icon = document.getAnonymousElementByAttribute(notification, "class", "popup-notification-icon"); 1.435 + is(notification.getAttribute("label"), notificationObj.message, "message matches"); 1.436 + is(notification.id, notificationObj.id + "-notification", "id matches"); 1.437 + if (notificationObj.id != "identity-request" && notificationObj.mainAction) { 1.438 + is(notification.getAttribute("buttonlabel"), notificationObj.mainAction.label, "main action label matches"); 1.439 + is(notification.getAttribute("buttonaccesskey"), notificationObj.mainAction.accessKey, "main action accesskey matches"); 1.440 + } 1.441 + let actualSecondaryActions = notification.childNodes; 1.442 + let secondaryActions = notificationObj.secondaryActions || []; 1.443 + let actualSecondaryActionsCount = actualSecondaryActions.length; 1.444 + if (secondaryActions.length) { 1.445 + let lastChild = actualSecondaryActions.item(actualSecondaryActions.length - 1); 1.446 + is(lastChild.tagName, "menuseparator", "menuseparator exists"); 1.447 + actualSecondaryActionsCount--; 1.448 + } 1.449 + is(actualSecondaryActionsCount, secondaryActions.length, actualSecondaryActions.length + " secondary actions"); 1.450 + secondaryActions.forEach(function (a, i) { 1.451 + is(actualSecondaryActions[i].getAttribute("label"), a.label, "label for secondary action " + i + " matches"); 1.452 + is(actualSecondaryActions[i].getAttribute("accesskey"), a.accessKey, "accessKey for secondary action " + i + " matches"); 1.453 + }); 1.454 +} 1.455 + 1.456 +function triggerMainCommand(popup) { 1.457 + info("[Test #" + gTestIndex + "] triggering main command"); 1.458 + let notifications = popup.childNodes; 1.459 + ok(notifications.length > 0, "at least one notification displayed"); 1.460 + let notification = notifications[0]; 1.461 + 1.462 + // 20, 10 so that the inner button is hit 1.463 + EventUtils.synthesizeMouse(notification.button, 20, 10, {}); 1.464 +} 1.465 + 1.466 +function triggerSecondaryCommand(popup, index) { 1.467 + info("[Test #" + gTestIndex + "] triggering secondary command"); 1.468 + let notifications = popup.childNodes; 1.469 + ok(notifications.length > 0, "at least one notification displayed"); 1.470 + let notification = notifications[0]; 1.471 + 1.472 + notification.button.focus(); 1.473 + 1.474 + popup.addEventListener("popupshown", function () { 1.475 + popup.removeEventListener("popupshown", arguments.callee, false); 1.476 + 1.477 + // Press down until the desired command is selected 1.478 + for (let i = 0; i <= index; i++) 1.479 + EventUtils.synthesizeKey("VK_DOWN", {}); 1.480 + 1.481 + // Activate 1.482 + EventUtils.synthesizeKey("VK_RETURN", {}); 1.483 + }, false); 1.484 + 1.485 + // One down event to open the popup 1.486 + EventUtils.synthesizeKey("VK_DOWN", { altKey: (navigator.platform.indexOf("Mac") == -1) }); 1.487 +} 1.488 + 1.489 +function dismissNotification(popup) { 1.490 + info("[Test #" + gTestIndex + "] dismissing notification"); 1.491 + executeSoon(function () { 1.492 + EventUtils.synthesizeKey("VK_ESCAPE", {}); 1.493 + }); 1.494 +} 1.495 + 1.496 +function partial(fn) { 1.497 + let args = Array.prototype.slice.call(arguments, 1); 1.498 + return function() { 1.499 + return fn.apply(this, args.concat(Array.prototype.slice.call(arguments))); 1.500 + }; 1.501 +} 1.502 + 1.503 +// create a mock "doc" object, which the Identity Service 1.504 +// uses as a pointer back into the doc object 1.505 +function mock_doc(aIdentity, aOrigin, aDoFunc) { 1.506 + let mockedDoc = {}; 1.507 + mockedDoc.id = outerWinId; 1.508 + mockedDoc.loggedInEmail = aIdentity; 1.509 + mockedDoc.origin = aOrigin; 1.510 + mockedDoc['do'] = aDoFunc; 1.511 + mockedDoc.doReady = partial(aDoFunc, 'ready'); 1.512 + mockedDoc.doLogin = partial(aDoFunc, 'login'); 1.513 + mockedDoc.doLogout = partial(aDoFunc, 'logout'); 1.514 + mockedDoc.doError = partial(aDoFunc, 'error'); 1.515 + mockedDoc.doCancel = partial(aDoFunc, 'cancel'); 1.516 + mockedDoc.doCoffee = partial(aDoFunc, 'coffee'); 1.517 + 1.518 + return mockedDoc; 1.519 +} 1.520 + 1.521 +// takes a list of functions and returns a function that 1.522 +// when called the first time, calls the first func, 1.523 +// then the next time the second, etc. 1.524 +function call_sequentially() { 1.525 + let numCalls = 0; 1.526 + let funcs = arguments; 1.527 + 1.528 + return function() { 1.529 + if (!funcs[numCalls]) { 1.530 + let argString = Array.prototype.slice.call(arguments).join(","); 1.531 + ok(false, "Too many calls: " + argString); 1.532 + return; 1.533 + } 1.534 + funcs[numCalls].apply(funcs[numCalls], arguments); 1.535 + numCalls += 1; 1.536 + }; 1.537 +} 1.538 + 1.539 +function setupRPFlow(aIdentity) { 1.540 + IdentityService.RP.watch(mock_doc(aIdentity, TEST_ORIGIN, call_sequentially( 1.541 + function(action, params) { 1.542 + is(action, "ready", "1st callback"); 1.543 + is(params, null); 1.544 + }, 1.545 + function(action, params) { 1.546 + is(action, "logout", "2nd callback"); 1.547 + is(params, null); 1.548 + }, 1.549 + function(action, params) { 1.550 + is(action, "ready", "3rd callback"); 1.551 + is(params, null); 1.552 + } 1.553 + ))); 1.554 +} 1.555 + 1.556 +function WindowObserver(aCallback, aObserveTopic = "domwindowopened") { 1.557 + this.observe = function(aSubject, aTopic, aData) { 1.558 + if (aTopic != aObserveTopic) { 1.559 + return; 1.560 + } 1.561 + info(aObserveTopic); 1.562 + Services.ww.unregisterNotification(this); 1.563 + 1.564 + SimpleTest.executeSoon(function() { 1.565 + let domWin = aSubject.QueryInterface(Ci.nsIDOMWindow); 1.566 + aCallback(domWin); 1.567 + }); 1.568 + }; 1.569 +}