|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 * http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 "use strict"; |
|
5 |
|
6 /** |
|
7 * TO TEST: |
|
8 * - test state saved on doorhanger dismissal |
|
9 * - links to switch steps |
|
10 * - TOS and PP link clicks |
|
11 * - identityList is populated correctly |
|
12 */ |
|
13 |
|
14 Services.prefs.setBoolPref("toolkit.identity.debug", true); |
|
15 |
|
16 XPCOMUtils.defineLazyModuleGetter(this, "IdentityService", |
|
17 "resource://gre/modules/identity/Identity.jsm"); |
|
18 |
|
19 const TEST_ORIGIN = "https://example.com"; |
|
20 const TEST_EMAIL = "user@example.com"; |
|
21 |
|
22 let gTestIndex = 0; |
|
23 let outerWinId = gBrowser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) |
|
24 .getInterface(Ci.nsIDOMWindowUtils).outerWindowID; |
|
25 |
|
26 function NotificationBase(aNotId) { |
|
27 this.id = aNotId; |
|
28 } |
|
29 NotificationBase.prototype = { |
|
30 message: TEST_ORIGIN, |
|
31 mainAction: { |
|
32 label: "", |
|
33 callback: function() { |
|
34 this.mainActionClicked = true; |
|
35 }.bind(this), |
|
36 }, |
|
37 secondaryActions: [], |
|
38 options: { |
|
39 "identity": { |
|
40 origin: TEST_ORIGIN, |
|
41 rpId: outerWinId, |
|
42 }, |
|
43 }, |
|
44 }; |
|
45 |
|
46 let tests = [ |
|
47 { |
|
48 name: "test_request_required_typed", |
|
49 |
|
50 run: function() { |
|
51 setupRPFlow(); |
|
52 this.notifyOptions = { |
|
53 rpId: outerWinId, |
|
54 origin: TEST_ORIGIN, |
|
55 }; |
|
56 this.notifyObj = new NotificationBase("identity-request"); |
|
57 Services.obs.notifyObservers({wrappedJSObject: this.notifyOptions}, |
|
58 "identity-request", null); |
|
59 }, |
|
60 |
|
61 onShown: function(popup) { |
|
62 checkPopup(popup, this.notifyObj); |
|
63 let notification = popup.childNodes[0]; |
|
64 |
|
65 // Check identity popup state |
|
66 let state = notification.identity; |
|
67 ok(!state.typedEmail, "Nothing should be typed yet"); |
|
68 ok(!state.selected, "Identity should not be selected yet"); |
|
69 ok(!state.termsOfService, "No TOS specified"); |
|
70 ok(!state.privacyPolicy, "No PP specified"); |
|
71 is(state.step, 0, "Step should be persisted with default value"); |
|
72 is(state.rpId, outerWinId, "Check rpId"); |
|
73 is(state.origin, TEST_ORIGIN, "Check origin"); |
|
74 |
|
75 is(notification.step, 0, "Should be on the new email step"); |
|
76 is(notification.chooseEmailLink.hidden, true, "Identity list is empty so link to list view should be hidden"); |
|
77 is(notification.addEmailLink.parentElement.hidden, true, "We are already on the email input step so choose email pane should be hidden"); |
|
78 is(notification.emailField.value, "", "Email field should default to empty on a new notification"); |
|
79 let notifDoc = notification.ownerDocument; |
|
80 ok(notifDoc.getAnonymousElementByAttribute(notification, "anonid", "tos").hidden, |
|
81 "TOS link should be hidden"); |
|
82 ok(notifDoc.getAnonymousElementByAttribute(notification, "anonid", "privacypolicy").hidden, |
|
83 "PP link should be hidden"); |
|
84 |
|
85 // Try to continue with a missing email address |
|
86 triggerMainCommand(popup); |
|
87 is(notification.throbber.style.visibility, "hidden", "is throbber visible"); |
|
88 ok(!notification.button.disabled, "Button should not be disabled"); |
|
89 is(window.gIdentitySelected, null, "Check no identity selected"); |
|
90 |
|
91 // Fill in an invalid email address and try again |
|
92 notification.emailField.value = "foo"; |
|
93 triggerMainCommand(popup); |
|
94 is(notification.throbber.style.visibility, "hidden", "is throbber visible"); |
|
95 ok(!notification.button.disabled, "Button should not be disabled"); |
|
96 is(window.gIdentitySelected, null, "Check no identity selected"); |
|
97 |
|
98 // Fill in an email address and try again |
|
99 notification.emailField.value = TEST_EMAIL; |
|
100 triggerMainCommand(popup); |
|
101 is(window.gIdentitySelected.rpId, outerWinId, "Check identity selected rpId"); |
|
102 is(window.gIdentitySelected.identity, TEST_EMAIL, "Check identity selected email"); |
|
103 is(notification.identity.selected, TEST_EMAIL, "Check persisted email"); |
|
104 is(notification.throbber.style.visibility, "visible", "is throbber visible"); |
|
105 ok(notification.button.disabled, "Button should be disabled"); |
|
106 ok(notification.emailField.disabled, "Email field should be disabled"); |
|
107 ok(notification.identityList.disabled, "Identity list should be disabled"); |
|
108 |
|
109 PopupNotifications.getNotification("identity-request").remove(); |
|
110 }, |
|
111 |
|
112 onHidden: function(popup) { }, |
|
113 }, |
|
114 { |
|
115 name: "test_request_optional", |
|
116 |
|
117 run: function() { |
|
118 this.notifyOptions = { |
|
119 rpId: outerWinId, |
|
120 origin: TEST_ORIGIN, |
|
121 privacyPolicy: TEST_ORIGIN + "/pp.txt", |
|
122 termsOfService: TEST_ORIGIN + "/tos.tzt", |
|
123 }; |
|
124 this.notifyObj = new NotificationBase("identity-request"); |
|
125 Services.obs.notifyObservers({ wrappedJSObject: this.notifyOptions }, |
|
126 "identity-request", null); |
|
127 }, |
|
128 |
|
129 onShown: function(popup) { |
|
130 checkPopup(popup, this.notifyObj); |
|
131 let notification = popup.childNodes[0]; |
|
132 |
|
133 // Check identity popup state |
|
134 let state = notification.identity; |
|
135 ok(!state.typedEmail, "Nothing should be typed yet"); |
|
136 ok(!state.selected, "Identity should not be selected yet"); |
|
137 is(state.termsOfService, this.notifyOptions.termsOfService, "Check TOS URL"); |
|
138 is(state.privacyPolicy, this.notifyOptions.privacyPolicy, "Check PP URL"); |
|
139 is(state.step, 0, "Step should be persisted with default value"); |
|
140 is(state.rpId, outerWinId, "Check rpId"); |
|
141 is(state.origin, TEST_ORIGIN, "Check origin"); |
|
142 |
|
143 is(notification.step, 0, "Should be on the new email step"); |
|
144 is(notification.chooseEmailLink.hidden, true, "Identity list is empty so link to list view should be hidden"); |
|
145 is(notification.addEmailLink.parentElement.hidden, true, "We are already on the email input step so choose email pane should be hidden"); |
|
146 is(notification.emailField.value, "", "Email field should default to empty on a new notification"); |
|
147 let notifDoc = notification.ownerDocument; |
|
148 let tosLink = notifDoc.getAnonymousElementByAttribute(notification, "anonid", "tos"); |
|
149 ok(!tosLink.hidden, "TOS link should be visible"); |
|
150 is(tosLink.href, this.notifyOptions.termsOfService, "Check TOS link URL"); |
|
151 let ppLink = notifDoc.getAnonymousElementByAttribute(notification, "anonid", "privacypolicy"); |
|
152 ok(!ppLink.hidden, "PP link should be visible"); |
|
153 is(ppLink.href, this.notifyOptions.privacyPolicy, "Check PP link URL"); |
|
154 |
|
155 // Try to continue with a missing email address |
|
156 triggerMainCommand(popup); |
|
157 is(notification.throbber.style.visibility, "hidden", "is throbber visible"); |
|
158 ok(!notification.button.disabled, "Button should not be disabled"); |
|
159 is(window.gIdentitySelected, null, "Check no identity selected"); |
|
160 |
|
161 // Fill in an invalid email address and try again |
|
162 notification.emailField.value = "foo"; |
|
163 triggerMainCommand(popup); |
|
164 is(notification.throbber.style.visibility, "hidden", "is throbber visible"); |
|
165 ok(!notification.button.disabled, "Button should not be disabled"); |
|
166 is(window.gIdentitySelected, null, "Check no identity selected"); |
|
167 |
|
168 // Fill in an email address and try again |
|
169 notification.emailField.value = TEST_EMAIL; |
|
170 triggerMainCommand(popup); |
|
171 is(window.gIdentitySelected.rpId, outerWinId, "Check identity selected rpId"); |
|
172 is(window.gIdentitySelected.identity, TEST_EMAIL, "Check identity selected email"); |
|
173 is(notification.identity.selected, TEST_EMAIL, "Check persisted email"); |
|
174 is(notification.throbber.style.visibility, "visible", "is throbber visible"); |
|
175 ok(notification.button.disabled, "Button should be disabled"); |
|
176 ok(notification.emailField.disabled, "Email field should be disabled"); |
|
177 ok(notification.identityList.disabled, "Identity list should be disabled"); |
|
178 |
|
179 PopupNotifications.getNotification("identity-request").remove(); |
|
180 }, |
|
181 |
|
182 onHidden: function(popup) {}, |
|
183 }, |
|
184 { |
|
185 name: "test_login_state_changed", |
|
186 run: function () { |
|
187 this.notifyOptions = { |
|
188 rpId: outerWinId, |
|
189 }; |
|
190 this.notifyObj = new NotificationBase("identity-logged-in"); |
|
191 this.notifyObj.message = "Signed in as: user@example.com"; |
|
192 this.notifyObj.mainAction.label = "Sign Out"; |
|
193 this.notifyObj.mainAction.accessKey = "O"; |
|
194 Services.obs.notifyObservers({ wrappedJSObject: this.notifyOptions }, |
|
195 "identity-login-state-changed", TEST_EMAIL); |
|
196 executeSoon(function() { |
|
197 PopupNotifications.getNotification("identity-logged-in").anchorElement.click(); |
|
198 }); |
|
199 }, |
|
200 |
|
201 onShown: function(popup) { |
|
202 checkPopup(popup, this.notifyObj); |
|
203 |
|
204 // Fire the notification that the user is no longer logged-in to close the UI. |
|
205 Services.obs.notifyObservers({ wrappedJSObject: this.notifyOptions }, |
|
206 "identity-login-state-changed", null); |
|
207 }, |
|
208 |
|
209 onHidden: function(popup) {}, |
|
210 }, |
|
211 { |
|
212 name: "test_login_state_changed_logout", |
|
213 run: function () { |
|
214 this.notifyOptions = { |
|
215 rpId: outerWinId, |
|
216 }; |
|
217 this.notifyObj = new NotificationBase("identity-logged-in"); |
|
218 this.notifyObj.message = "Signed in as: user@example.com"; |
|
219 this.notifyObj.mainAction.label = "Sign Out"; |
|
220 this.notifyObj.mainAction.accessKey = "O"; |
|
221 Services.obs.notifyObservers({ wrappedJSObject: this.notifyOptions }, |
|
222 "identity-login-state-changed", TEST_EMAIL); |
|
223 executeSoon(function() { |
|
224 PopupNotifications.getNotification("identity-logged-in").anchorElement.click(); |
|
225 }); |
|
226 }, |
|
227 |
|
228 onShown: function(popup) { |
|
229 checkPopup(popup, this.notifyObj); |
|
230 |
|
231 // This time trigger the Sign Out button and make sure the UI goes away. |
|
232 triggerMainCommand(popup); |
|
233 }, |
|
234 |
|
235 onHidden: function(popup) {}, |
|
236 }, |
|
237 ]; |
|
238 |
|
239 function test_auth() { |
|
240 let notifyOptions = { |
|
241 provId: outerWinId, |
|
242 origin: TEST_ORIGIN, |
|
243 }; |
|
244 |
|
245 Services.obs.addObserver(function() { |
|
246 // prepare to send auth-complete and close the window |
|
247 let winCloseObs = new WindowObserver(function(closedWin) { |
|
248 info("closed window"); |
|
249 finish(); |
|
250 }, "domwindowclosed"); |
|
251 Services.ww.registerNotification(winCloseObs); |
|
252 Services.obs.notifyObservers(null, "identity-auth-complete", IdentityService.IDP.authenticationFlowSet.authId); |
|
253 |
|
254 }, "test-identity-auth-window", false); |
|
255 |
|
256 let winObs = new WindowObserver(function(authWin) { |
|
257 ok(authWin, "Authentication window opened"); |
|
258 ok(authWin.contentWindow.location); |
|
259 }); |
|
260 |
|
261 Services.ww.registerNotification(winObs); |
|
262 |
|
263 Services.obs.notifyObservers({ wrappedJSObject: notifyOptions }, |
|
264 "identity-auth", TEST_ORIGIN + "/auth"); |
|
265 } |
|
266 |
|
267 function test() { |
|
268 waitForExplicitFinish(); |
|
269 |
|
270 let sitw = {}; |
|
271 try { |
|
272 Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw); |
|
273 } catch (ex) { |
|
274 ok(true, "Skip the test since SignInToWebsite.jsm isn't packaged outside outside mozilla-central"); |
|
275 finish(); |
|
276 return; |
|
277 } |
|
278 |
|
279 PopupNotifications.transitionsEnabled = false; |
|
280 |
|
281 registerCleanupFunction(cleanUp); |
|
282 |
|
283 ok(sitw.SignInToWebsiteUX, "SignInToWebsiteUX object exists"); |
|
284 if (!Services.prefs.getBoolPref("dom.identity.enabled")) { |
|
285 // If the pref isn't enabled then init wasn't called so do that for the test. |
|
286 sitw.SignInToWebsiteUX.init(); |
|
287 } |
|
288 |
|
289 // Replace implementation of ID Service functions for testing |
|
290 window.selectIdentity = sitw.SignInToWebsiteUX.selectIdentity; |
|
291 sitw.SignInToWebsiteUX.selectIdentity = function(aRpId, aIdentity) { |
|
292 info("Identity selected: " + aIdentity); |
|
293 window.gIdentitySelected = {rpId: aRpId, identity: aIdentity}; |
|
294 }; |
|
295 |
|
296 window.setAuthenticationFlow = IdentityService.IDP.setAuthenticationFlow; |
|
297 IdentityService.IDP.setAuthenticationFlow = function(aAuthId, aProvId) { |
|
298 info("setAuthenticationFlow: " + aAuthId + " : " + aProvId); |
|
299 this.authenticationFlowSet = { authId: aAuthId, provId: aProvId }; |
|
300 Services.obs.notifyObservers(null, "test-identity-auth-window", aAuthId); |
|
301 }; |
|
302 |
|
303 runNextTest(); |
|
304 } |
|
305 |
|
306 // Cleanup between tests |
|
307 function resetState() { |
|
308 delete window.gIdentitySelected; |
|
309 delete IdentityService.IDP.authenticationFlowSet; |
|
310 IdentityService.reset(); |
|
311 } |
|
312 |
|
313 // Cleanup after all tests |
|
314 function cleanUp() { |
|
315 info("cleanup"); |
|
316 resetState(); |
|
317 |
|
318 PopupNotifications.transitionsEnabled = true; |
|
319 |
|
320 for (let topic in gActiveObservers) |
|
321 Services.obs.removeObserver(gActiveObservers[topic], topic); |
|
322 for (let eventName in gActiveListeners) |
|
323 PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false); |
|
324 delete IdentityService.RP._rpFlows[outerWinId]; |
|
325 |
|
326 // Put the JSM functions back to how they were |
|
327 IdentityService.IDP.setAuthenticationFlow = window.setAuthenticationFlow; |
|
328 delete window.setAuthenticationFlow; |
|
329 |
|
330 let sitw = {}; |
|
331 Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw); |
|
332 sitw.SignInToWebsiteUX.selectIdentity = window.selectIdentity; |
|
333 delete window.selectIdentity; |
|
334 if (!Services.prefs.getBoolPref("dom.identity.enabled")) { |
|
335 sitw.SignInToWebsiteUX.uninit(); |
|
336 } |
|
337 |
|
338 Services.prefs.clearUserPref("toolkit.identity.debug"); |
|
339 } |
|
340 |
|
341 let gActiveListeners = {}; |
|
342 let gActiveObservers = {}; |
|
343 let gShownState = {}; |
|
344 |
|
345 function runNextTest() { |
|
346 let nextTest = tests[gTestIndex]; |
|
347 |
|
348 function goNext() { |
|
349 resetState(); |
|
350 if (++gTestIndex == tests.length) |
|
351 executeSoon(test_auth); |
|
352 else |
|
353 executeSoon(runNextTest); |
|
354 } |
|
355 |
|
356 function addObserver(topic) { |
|
357 function observer() { |
|
358 Services.obs.removeObserver(observer, "PopupNotifications-" + topic); |
|
359 delete gActiveObservers["PopupNotifications-" + topic]; |
|
360 |
|
361 info("[Test #" + gTestIndex + "] observer for " + topic + " called"); |
|
362 nextTest[topic](); |
|
363 goNext(); |
|
364 } |
|
365 Services.obs.addObserver(observer, "PopupNotifications-" + topic, false); |
|
366 gActiveObservers["PopupNotifications-" + topic] = observer; |
|
367 } |
|
368 |
|
369 if (nextTest.backgroundShow) { |
|
370 addObserver("backgroundShow"); |
|
371 } else if (nextTest.updateNotShowing) { |
|
372 addObserver("updateNotShowing"); |
|
373 } else { |
|
374 doOnPopupEvent("popupshowing", function () { |
|
375 info("[Test #" + gTestIndex + "] popup showing"); |
|
376 }); |
|
377 doOnPopupEvent("popupshown", function () { |
|
378 gShownState[gTestIndex] = true; |
|
379 info("[Test #" + gTestIndex + "] popup shown"); |
|
380 nextTest.onShown(this); |
|
381 }); |
|
382 |
|
383 // We allow multiple onHidden functions to be defined in an array. They're |
|
384 // called in the order they appear. |
|
385 let onHiddenArray = nextTest.onHidden instanceof Array ? |
|
386 nextTest.onHidden : |
|
387 [nextTest.onHidden]; |
|
388 doOnPopupEvent("popuphidden", function () { |
|
389 if (!gShownState[gTestIndex]) { |
|
390 // TODO: needed? |
|
391 info("Popup from test " + gTestIndex + " was hidden before its popupshown fired"); |
|
392 } |
|
393 |
|
394 let onHidden = onHiddenArray.shift(); |
|
395 info("[Test #" + gTestIndex + "] popup hidden (" + onHiddenArray.length + " hides remaining)"); |
|
396 executeSoon(function () { |
|
397 onHidden.call(nextTest, this); |
|
398 if (!onHiddenArray.length) |
|
399 goNext(); |
|
400 }.bind(this)); |
|
401 }, onHiddenArray.length); |
|
402 info("[Test #" + gTestIndex + "] added listeners; panel state: " + PopupNotifications.isPanelOpen); |
|
403 } |
|
404 |
|
405 info("[Test #" + gTestIndex + "] running test"); |
|
406 nextTest.run(); |
|
407 } |
|
408 |
|
409 function doOnPopupEvent(eventName, callback, numExpected) { |
|
410 gActiveListeners[eventName] = function (event) { |
|
411 if (event.target != PopupNotifications.panel) |
|
412 return; |
|
413 if (typeof(numExpected) === "number") |
|
414 numExpected--; |
|
415 if (!numExpected) { |
|
416 PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false); |
|
417 delete gActiveListeners[eventName]; |
|
418 } |
|
419 |
|
420 callback.call(PopupNotifications.panel); |
|
421 }; |
|
422 PopupNotifications.panel.addEventListener(eventName, gActiveListeners[eventName], false); |
|
423 } |
|
424 |
|
425 function checkPopup(popup, notificationObj) { |
|
426 info("[Test #" + gTestIndex + "] checking popup"); |
|
427 |
|
428 let notifications = popup.childNodes; |
|
429 is(notifications.length, 1, "only one notification displayed"); |
|
430 let notification = notifications[0]; |
|
431 let icon = document.getAnonymousElementByAttribute(notification, "class", "popup-notification-icon"); |
|
432 is(notification.getAttribute("label"), notificationObj.message, "message matches"); |
|
433 is(notification.id, notificationObj.id + "-notification", "id matches"); |
|
434 if (notificationObj.id != "identity-request" && notificationObj.mainAction) { |
|
435 is(notification.getAttribute("buttonlabel"), notificationObj.mainAction.label, "main action label matches"); |
|
436 is(notification.getAttribute("buttonaccesskey"), notificationObj.mainAction.accessKey, "main action accesskey matches"); |
|
437 } |
|
438 let actualSecondaryActions = notification.childNodes; |
|
439 let secondaryActions = notificationObj.secondaryActions || []; |
|
440 let actualSecondaryActionsCount = actualSecondaryActions.length; |
|
441 if (secondaryActions.length) { |
|
442 let lastChild = actualSecondaryActions.item(actualSecondaryActions.length - 1); |
|
443 is(lastChild.tagName, "menuseparator", "menuseparator exists"); |
|
444 actualSecondaryActionsCount--; |
|
445 } |
|
446 is(actualSecondaryActionsCount, secondaryActions.length, actualSecondaryActions.length + " secondary actions"); |
|
447 secondaryActions.forEach(function (a, i) { |
|
448 is(actualSecondaryActions[i].getAttribute("label"), a.label, "label for secondary action " + i + " matches"); |
|
449 is(actualSecondaryActions[i].getAttribute("accesskey"), a.accessKey, "accessKey for secondary action " + i + " matches"); |
|
450 }); |
|
451 } |
|
452 |
|
453 function triggerMainCommand(popup) { |
|
454 info("[Test #" + gTestIndex + "] triggering main command"); |
|
455 let notifications = popup.childNodes; |
|
456 ok(notifications.length > 0, "at least one notification displayed"); |
|
457 let notification = notifications[0]; |
|
458 |
|
459 // 20, 10 so that the inner button is hit |
|
460 EventUtils.synthesizeMouse(notification.button, 20, 10, {}); |
|
461 } |
|
462 |
|
463 function triggerSecondaryCommand(popup, index) { |
|
464 info("[Test #" + gTestIndex + "] triggering secondary command"); |
|
465 let notifications = popup.childNodes; |
|
466 ok(notifications.length > 0, "at least one notification displayed"); |
|
467 let notification = notifications[0]; |
|
468 |
|
469 notification.button.focus(); |
|
470 |
|
471 popup.addEventListener("popupshown", function () { |
|
472 popup.removeEventListener("popupshown", arguments.callee, false); |
|
473 |
|
474 // Press down until the desired command is selected |
|
475 for (let i = 0; i <= index; i++) |
|
476 EventUtils.synthesizeKey("VK_DOWN", {}); |
|
477 |
|
478 // Activate |
|
479 EventUtils.synthesizeKey("VK_RETURN", {}); |
|
480 }, false); |
|
481 |
|
482 // One down event to open the popup |
|
483 EventUtils.synthesizeKey("VK_DOWN", { altKey: (navigator.platform.indexOf("Mac") == -1) }); |
|
484 } |
|
485 |
|
486 function dismissNotification(popup) { |
|
487 info("[Test #" + gTestIndex + "] dismissing notification"); |
|
488 executeSoon(function () { |
|
489 EventUtils.synthesizeKey("VK_ESCAPE", {}); |
|
490 }); |
|
491 } |
|
492 |
|
493 function partial(fn) { |
|
494 let args = Array.prototype.slice.call(arguments, 1); |
|
495 return function() { |
|
496 return fn.apply(this, args.concat(Array.prototype.slice.call(arguments))); |
|
497 }; |
|
498 } |
|
499 |
|
500 // create a mock "doc" object, which the Identity Service |
|
501 // uses as a pointer back into the doc object |
|
502 function mock_doc(aIdentity, aOrigin, aDoFunc) { |
|
503 let mockedDoc = {}; |
|
504 mockedDoc.id = outerWinId; |
|
505 mockedDoc.loggedInEmail = aIdentity; |
|
506 mockedDoc.origin = aOrigin; |
|
507 mockedDoc['do'] = aDoFunc; |
|
508 mockedDoc.doReady = partial(aDoFunc, 'ready'); |
|
509 mockedDoc.doLogin = partial(aDoFunc, 'login'); |
|
510 mockedDoc.doLogout = partial(aDoFunc, 'logout'); |
|
511 mockedDoc.doError = partial(aDoFunc, 'error'); |
|
512 mockedDoc.doCancel = partial(aDoFunc, 'cancel'); |
|
513 mockedDoc.doCoffee = partial(aDoFunc, 'coffee'); |
|
514 |
|
515 return mockedDoc; |
|
516 } |
|
517 |
|
518 // takes a list of functions and returns a function that |
|
519 // when called the first time, calls the first func, |
|
520 // then the next time the second, etc. |
|
521 function call_sequentially() { |
|
522 let numCalls = 0; |
|
523 let funcs = arguments; |
|
524 |
|
525 return function() { |
|
526 if (!funcs[numCalls]) { |
|
527 let argString = Array.prototype.slice.call(arguments).join(","); |
|
528 ok(false, "Too many calls: " + argString); |
|
529 return; |
|
530 } |
|
531 funcs[numCalls].apply(funcs[numCalls], arguments); |
|
532 numCalls += 1; |
|
533 }; |
|
534 } |
|
535 |
|
536 function setupRPFlow(aIdentity) { |
|
537 IdentityService.RP.watch(mock_doc(aIdentity, TEST_ORIGIN, call_sequentially( |
|
538 function(action, params) { |
|
539 is(action, "ready", "1st callback"); |
|
540 is(params, null); |
|
541 }, |
|
542 function(action, params) { |
|
543 is(action, "logout", "2nd callback"); |
|
544 is(params, null); |
|
545 }, |
|
546 function(action, params) { |
|
547 is(action, "ready", "3rd callback"); |
|
548 is(params, null); |
|
549 } |
|
550 ))); |
|
551 } |
|
552 |
|
553 function WindowObserver(aCallback, aObserveTopic = "domwindowopened") { |
|
554 this.observe = function(aSubject, aTopic, aData) { |
|
555 if (aTopic != aObserveTopic) { |
|
556 return; |
|
557 } |
|
558 info(aObserveTopic); |
|
559 Services.ww.unregisterNotification(this); |
|
560 |
|
561 SimpleTest.executeSoon(function() { |
|
562 let domWin = aSubject.QueryInterface(Ci.nsIDOMWindow); |
|
563 aCallback(domWin); |
|
564 }); |
|
565 }; |
|
566 } |