Bug 1014332 test cleanups that better address some async issues, r=jaws
authorShane Caraveo <scaraveo@mozilla.com>
Thu, 09 Oct 2014 12:02:35 -0700
changeset 209698 a99ff9ef5192c73e4c2202edacb17e387f65bc6c
parent 209697 794f55a6372b4fec36de47fba0af77518f080311
child 209699 d065a508d21688e15499d43e3e38e0e550d39a9d
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjaws
bugs1014332
milestone35.0a1
Bug 1014332 test cleanups that better address some async issues, r=jaws
browser/base/content/test/social/browser_aboutHome_activation.js
browser/base/content/test/social/browser_share.js
browser/base/content/test/social/browser_social_chatwindow.js
browser/base/content/test/social/browser_social_errorPage.js
browser/base/content/test/social/head.js
--- a/browser/base/content/test/social/browser_aboutHome_activation.js
+++ b/browser/base/content/test/social/browser_aboutHome_activation.js
@@ -14,17 +14,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 let snippet =
 '     <script>' +
 '       var manifest = {' +
 '         "name": "Demo Social Service",' +
 '         "origin": "https://example.com",' +
 '         "iconURL": "chrome://branding/content/icon16.png",' +
 '         "icon32URL": "chrome://branding/content/favicon32.png",' +
 '         "icon64URL": "chrome://branding/content/icon64.png",' +
-'         "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",' +
+'         "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",' +
 '         "postActivationURL": "https://example.com/browser/browser/base/content/test/social/social_postActivation.html",' +
 '       };' +
 '       function activateProvider(node) {' +
 '         node.setAttribute("data-service", JSON.stringify(manifest));' +
 '         var event = new CustomEvent("ActivateSocialFeature");' +
 '         node.dispatchEvent(event);' +
 '       }' +
 '     </script>' +
@@ -36,17 +36,17 @@ let snippet =
 let snippet2 =
 '     <script>' +
 '       var manifest = {' +
 '         "name": "Demo Social Service",' +
 '         "origin": "https://example.com",' +
 '         "iconURL": "chrome://branding/content/icon16.png",' +
 '         "icon32URL": "chrome://branding/content/favicon32.png",' +
 '         "icon64URL": "chrome://branding/content/icon64.png",' +
-'         "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",' +
+'         "sidebarURL": "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",' +
 '         "postActivationURL": "https://example.com/browser/browser/base/content/test/social/social_postActivation.html",' +
 '         "oneclick": true' +
 '       };' +
 '       function activateProvider(node) {' +
 '         node.setAttribute("data-service", JSON.stringify(manifest));' +
 '         var event = new CustomEvent("ActivateSocialFeature");' +
 '         node.dispatchEvent(event);' +
 '       }' +
--- a/browser/base/content/test/social/browser_share.js
+++ b/browser/base/content/test/social/browser_share.js
@@ -7,44 +7,36 @@ let manifest = { // normal provider
   name: "provider 1",
   origin: "https://example.com",
   workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
   iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
   shareURL: "https://example.com/browser/browser/base/content/test/social/share.html"
 };
 let activationPage = "https://example.com/browser/browser/base/content/test/social/share_activate.html";
 
-function waitForProviderEnabled(cb) {
-  Services.obs.addObserver(function providerSet(subject, topic, data) {
-    Services.obs.removeObserver(providerSet, "social:provider-enabled");
-    info("social:provider-enabled observer was notified");
-    cb();
-  }, "social:provider-enabled", false);
-}
-
-function sendActivationEvent(subframe, callback) {
+function sendActivationEvent(subframe) {
   // hack Social.lastEventReceived so we don't hit the "too many events" check.
   Social.lastEventReceived = 0;
   let doc = subframe.contentDocument;
   // if our test has a frame, use it
   let button = doc.getElementById("activation");
   ok(!!button, "got the activation button");
   EventUtils.synthesizeMouseAtCenter(button, {}, doc.defaultView);
-  if (callback)
-    executeSoon(callback);
 }
 
-function waitForEvent(iframe, eventName, callback) {
+function promiseShareFrameEvent(iframe, eventName) {
+  let deferred = Promise.defer();
   iframe.addEventListener(eventName, function load() {
     info("page load is " + iframe.contentDocument.location.href);
     if (iframe.contentDocument.location.href != "data:text/plain;charset=utf8,") {
       iframe.removeEventListener(eventName, load, true);
-      executeSoon(callback);
+      deferred.resolve();
     }
   }, true);
+  return deferred.promise;
 }
 
 function test() {
   waitForExplicitFinish();
   Services.prefs.setCharPref("social.shareDirectory", activationPage);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref("social.directories");
     Services.prefs.clearUserPref("social.shareDirectory");
@@ -111,25 +103,16 @@ let corpus = [
     options: {
       previews: ["http://example.com/1234/56789.jpg"],
       url: "http://www.example.com/photos/56789/",
       shortUrl: "http://imshort/p/abcde"
     }
   }
 ];
 
-function loadURLInTab(url, callback) {
-  info("Loading tab with "+url);
-  let tab = gBrowser.selectedTab = gBrowser.addTab(url);
-  waitForEvent(tab.linkedBrowser, "load", () => {
-    is(tab.linkedBrowser.currentURI.spec, url, "tab loaded")
-    callback(tab)
-  });
-}
-
 function hasoptions(testOptions, options) {
   let msg;
   for (let option in testOptions) {
     let data = testOptions[option];
     info("data: "+JSON.stringify(data));
     let message_data = options[option];
     info("message_data: "+JSON.stringify(message_data));
     if (Array.isArray(data)) {
@@ -170,17 +153,17 @@ var tests = {
     // starting from *some* page, share should be visible and enabled when
     // activating provider
     // initialize the button into the navbar
     CustomizableUI.addWidgetToArea("social-share-button", CustomizableUI.AREA_NAVBAR);
     // ensure correct state
     SocialUI.onCustomizeEnd(window);
 
     let testData = corpus[0];
-    loadURLInTab(testData.url, function(tab) {
+    addTab(testData.url, function(tab) {
       SocialService.addProvider(manifest, function(provider) {
         is(SocialUI.enabled, true, "SocialUI is enabled");
         checkSocialUI();
         // share should not be enabled since we only have about:blank page
         let shareButton = SocialShare.shareButton;
         // verify the attribute for proper css
         ok(!shareButton.hasAttribute("disabled"), "share button is enabled");
         // button should be visible
@@ -194,17 +177,17 @@ var tests = {
     let provider = Social._getProviderFromOrigin(manifest.origin);
     let port = provider.getWorkerPort();
     ok(port, "provider has a port");
     let testTab;
     let testIndex = 0;
     let testData = corpus[testIndex++];
 
     function runOneTest() {
-      loadURLInTab(testData.url, function(tab) {
+      addTab(testData.url, function(tab) {
         testTab = tab;
         SocialShare.sharePage(manifest.origin);
       });
     }
 
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
@@ -298,27 +281,27 @@ var tests = {
     let testTab;
     // cleared in the cleanup function
     Services.prefs.setCharPref("social.directories", "https://example.com");
     Services.prefs.setBoolPref("social.share.activationPanelEnabled", true);
     // make the iframe so we can wait on the load
     SocialShare._createFrame();
     let iframe = SocialShare.iframe;
 
-    waitForEvent(iframe, "load", () => {
+    promiseShareFrameEvent(iframe, "load").then(() => {
       let subframe = iframe.contentDocument.getElementById("activation-frame");
       waitForCondition(() => {
           // sometimes the iframe is ready before the panel is open, we need to
           // wait for both conditions
           return SocialShare.panel.state == "open"
                  && subframe.contentDocument
                  && subframe.contentDocument.readyState == "complete";
         }, () => {
         is(subframe.contentDocument.location.href, activationPage, "activation page loaded");
-        waitForProviderEnabled(() => {
+        promiseObserverNotified("social:provider-enabled").then(() => {
           let provider = Social._getProviderFromOrigin(manifest.origin);
           let port = provider.getWorkerPort();
           ok(!!port, "got port");
           port.onmessage = function (e) {
             let topic = e.data.topic;
             switch (topic) {
               case "got-share-data-message":
                 ok(true, "share completed");
@@ -327,14 +310,14 @@ var tests = {
                 break;
             }
           }
           port.postMessage({topic: "test-init"});
         });
         sendActivationEvent(subframe);
       }, "share panel did not open and load share page");
     });
-    loadURLInTab(activationPage, function(tab) {
+    addTab(activationPage, function(tab) {
       testTab = tab;
       SocialShare.sharePage();
     });
   }
 }
--- a/browser/base/content/test/social/browser_social_chatwindow.js
+++ b/browser/base/content/test/social/browser_social_chatwindow.js
@@ -23,67 +23,80 @@ let manifests = [
     name: "provider@test2",
     origin: "https://test2.example.com",
     sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html?test2",
     workerURL: "https://test2.example.com/browser/browser/base/content/test/social/social_worker.js",
     iconURL: "chrome://branding/content/icon48.png"
   }
 ];
 
+let ports = [];
+function getProviderPort(provider) {
+  let port = provider.getWorkerPort();
+  ok(port, "provider has a port");
+  ports.push(port);
+  return port;
+}
 let chatId = 0;
 function openChat(provider, callback) {
   let chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
-  let port = provider.getWorkerPort();
+  let port = getProviderPort(provider);
   port.onmessage = function(e) {
     if (e.data.topic == "got-chatbox-message") {
-      port.close();
       callback();
     }
   }
   let url = chatUrl + "?" + (chatId++);
   port.postMessage({topic: "test-init"});
   port.postMessage({topic: "test-worker-chat", data: url});
   gURLsNotRemembered.push(url);
+  return port;
 }
 
 function windowHasChats(win) {
   return !!getChatBar().firstElementChild;
 }
 
 function test() {
   requestLongerTimeout(2); // only debug builds seem to need more time...
   waitForExplicitFinish();
 
   let oldwidth = window.outerWidth; // we futz with these, so we restore them
   let oldleft = window.screenX;
   window.moveTo(0, window.screenY)
   let postSubTest = function(cb) {
+    // ensure ports are closed
+    for (let port of ports) {
+      port.close()
+      ok(port._closed, "port closed");
+    }
+    ports = [];
+
     let chats = document.getElementById("pinnedchats");
     ok(chats.children.length == 0, "no chatty children left behind");
     cb();
   };
   runSocialTestWithProvider(manifests, function (finishcb) {
     ok(Social.enabled, "Social is enabled");
-    ok(Social.providers[0].getWorkerPort(), "provider 0 has port");
-    ok(Social.providers[1].getWorkerPort(), "provider 1 has port");
-    ok(Social.providers[2].getWorkerPort(), "provider 2 has port");
+    ok(getProviderPort(Social.providers[0]), "provider 0 has port");
+    ok(getProviderPort(Social.providers[1]), "provider 1 has port");
+    ok(getProviderPort(Social.providers[2]), "provider 2 has port");
     SocialSidebar.show();
     runSocialTests(tests, undefined, postSubTest, function() {
       window.moveTo(oldleft, window.screenY)
       window.resizeTo(oldwidth, window.outerHeight);
       finishcb();
     });
   });
 }
 
 var tests = {
   testOpenCloseChat: function(next) {
     let chats = document.getElementById("pinnedchats");
-    let port = SocialSidebar.provider.getWorkerPort();
-    ok(port, "provider has a port");
+    let port = getProviderPort(SocialSidebar.provider);
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-sidebar-message":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "got-chatbox-visibility":
           if (e.data.result == "hidden") {
@@ -91,17 +104,16 @@ var tests = {
             chats.selectedChat.toggle();
           } else if (e.data.result == "shown") {
             ok(true, "chatbox got shown");
             // close it now
             let content = chats.selectedChat.content;
             content.addEventListener("unload", function chatUnload() {
               content.removeEventListener("unload", chatUnload, true);
               ok(true, "got chatbox unload on close");
-              port.close();
               next();
             }, true);
             chats.selectedChat.close();
           }
           break;
         case "got-chatbox-message":
           ok(true, "got chatbox message");
           ok(e.data.result == "ok", "got chatbox windowRef result: "+e.data.result);
@@ -109,42 +121,39 @@ var tests = {
           break;
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
   testWorkerChatWindow: function(next) {
     const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
     let chats = document.getElementById("pinnedchats");
-    let port = SocialSidebar.provider.getWorkerPort();
-    ok(port, "provider has a port");
+    let port = getProviderPort(SocialSidebar.provider);
     port.postMessage({topic: "test-init"});
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-chatbox-message":
           ok(true, "got a chat window opened");
           ok(chats.selectedChat, "chatbox from worker opened");
           while (chats.selectedChat) {
             chats.selectedChat.close();
           }
           ok(!chats.selectedChat, "chats are all closed");
           gURLsNotRemembered.push(chatUrl);
-          port.close();
           next();
           break;
       }
     }
     ok(!chats.selectedChat, "chats are all closed");
     port.postMessage({topic: "test-worker-chat", data: chatUrl});
   },
   testCloseSelf: function(next) {
     let chats = document.getElementById("pinnedchats");
-    let port = SocialSidebar.provider.getWorkerPort();
-    ok(port, "provider has a port");
+    let port = getProviderPort(SocialSidebar.provider);
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "got-chatbox-visibility":
           is(e.data.result, "shown", "chatbox shown");
@@ -152,31 +161,32 @@ var tests = {
           let chat = chats.selectedChat;
           ok(chat.parentNode, "chat has a parent node before it is closed");
           // ask it to close itself.
           let doc = chat.contentDocument;
           let evt = doc.createEvent("CustomEvent");
           evt.initCustomEvent("socialTest-CloseSelf", true, true, {});
           doc.documentElement.dispatchEvent(evt);
           ok(!chat.parentNode, "chat is now closed");
-          port.close();
           next();
           break;
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
 
   // Check what happens when you close the only visible chat.
   testCloseOnlyVisible: function(next) {
     let chatbar = getChatBar();
     let chatWidth = undefined;
     let num = 0;
     is(chatbar.childNodes.length, 0, "chatbar starting empty");
     is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
+    let port = getProviderPort(SocialSidebar.provider);
+    port.postMessage({topic: "test-init"});
 
     makeChat("normal", "first chat", function() {
       // got the first one.
       checkPopup();
       ok(chatbar.menupopup.parentNode.collapsed, "menu selection isn't visible");
       // we kinda cheat here and get the width of the first chat, assuming
       // that all future chats will have the same width when open.
       chatWidth = chatbar.calcTotalWidthOf(chatbar.selectedChat);
@@ -197,107 +207,102 @@ var tests = {
           closeAllChats();
           next();
         });
       });
     });
   },
 
   testShowWhenCollapsed: function(next) {
-    let port = SocialSidebar.provider.getWorkerPort();
+    let port = getProviderPort(SocialSidebar.provider);
     port.postMessage({topic: "test-init"});
     get3ChatsForCollapsing("normal", function(first, second, third) {
       let chatbar = getChatBar();
       chatbar.showChat(first);
       ok(!first.collapsed, "first should no longer be collapsed");
-      ok(second.collapsed ||  third.collapsed, false, "one of the others should be collapsed");
+      is(second.collapsed ||  third.collapsed, true, "one of the others should be collapsed");
       closeAllChats();
-      port.close();
       next();
     });
   },
 
   testOnlyOneCallback: function(next) {
     let chats = document.getElementById("pinnedchats");
-    let port = SocialSidebar.provider.getWorkerPort();
+    let port = getProviderPort(SocialSidebar.provider);
     let numOpened = 0;
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           port.postMessage({topic: "test-chatbox-open"});
           break;
         case "chatbox-opened":
           numOpened += 1;
           port.postMessage({topic: "ping"});
           break;
         case "pong":
           executeSoon(function() {
             is(numOpened, 1, "only got one open message");
             chats.selectedChat.close();
-            port.close();
             next();
           });
       }
     }
     port.postMessage({topic: "test-init", data: { id: 1 }});
   },
 
   testMultipleProviderChat: function(next) {
     // test incomming chats from all providers
-    openChat(Social.providers[0], function() {
-      openChat(Social.providers[1], function() {
-        openChat(Social.providers[2], function() {
+    let port0 = openChat(Social.providers[0], function() {
+      let port1 = openChat(Social.providers[1], function() {
+        let port2 = openChat(Social.providers[2], function() {
           let chats = document.getElementById("pinnedchats");
           waitForCondition(function() chats.children.length == Social.providers.length,
             function() {
               ok(true, "one chat window per provider opened");
               // test logout of a single provider
-              let provider = Social.providers[2];
-              let port = provider.getWorkerPort();
-              port.postMessage({topic: "test-logout"});
+              port2.postMessage({topic: "test-logout"});
               waitForCondition(function() chats.children.length == Social.providers.length - 1,
                 function() {
                   closeAllChats();
                   waitForCondition(function() chats.children.length == 0,
                                    function() {
                                     ok(!chats.selectedChat, "multiprovider chats are all closed");
-                                    port.close();
                                     next();
                                    },
                                    "chat windows didn't close");
                 },
                 "chat window didn't close");
             }, "chat windows did not open");
         });
       });
     });
   },
 
   // XXX - note this must be the last test until we restore the login state
   // between tests...
   testCloseOnLogout: function(next) {
     const chatUrl = SocialSidebar.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
     let port = SocialSidebar.provider.getWorkerPort();
+    ports.push(port);
     ok(port, "provider has a port");
     let opened = false;
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "test-init-done":
           info("open first chat window");
           port.postMessage({topic: "test-worker-chat", data: chatUrl});
           break;
         case "got-chatbox-message":
           ok(true, "got a chat window opened");
           if (opened) {
             port.postMessage({topic: "test-logout"});
             waitForCondition(function() document.getElementById("pinnedchats").firstChild == null,
                              function() {
-                              port.close();
                               next();
                              },
                              "chat windows didn't close");
           } else {
             // open a second chat window
             opened = true;
             port.postMessage({topic: "test-worker-chat", data: chatUrl+"?id=1"});
           }
--- a/browser/base/content/test/social/browser_social_errorPage.js
+++ b/browser/base/content/test/social/browser_social_errorPage.js
@@ -10,208 +10,217 @@ function gc() {
 }
 
 let openChatWindow = Cu.import("resource://gre/modules/MozSocialAPI.jsm", {}).openChatWindow;
 
 // Support for going on and offline.
 // (via browser/base/content/test/browser_bookmark_titles.js)
 let origProxyType = Services.prefs.getIntPref('network.proxy.type');
 
+function toggleOfflineStatus(goOffline) {
+  // Bug 968887 fix.  when going on/offline, wait for notification before continuing
+  let deferred = Promise.defer();
+  if (!goOffline) {
+    Services.prefs.setIntPref('network.proxy.type', origProxyType);
+  }
+  if (goOffline != Services.io.offline) {
+    info("initial offline state " + Services.io.offline);
+    let expect = !Services.io.offline;
+    Services.obs.addObserver(function offlineChange(subject, topic, data) {
+      Services.obs.removeObserver(offlineChange, "network:offline-status-changed");
+      info("offline state changed to " + Services.io.offline);
+      is(expect, Services.io.offline, "network:offline-status-changed successful toggle");
+      deferred.resolve();
+    }, "network:offline-status-changed", false);
+    BrowserOffline.toggleOfflineStatus();
+  } else {
+    deferred.resolve();
+  }
+  if (goOffline) {
+    Services.prefs.setIntPref('network.proxy.type', 0);
+    // LOAD_FLAGS_BYPASS_CACHE isn't good enough. So clear the cache.
+    Services.cache2.clear();
+  }
+  return deferred.promise;
+}
+
 function goOffline() {
   // Simulate a network outage with offline mode. (Localhost is still
   // accessible in offline mode, so disable the test proxy as well.)
-  if (!Services.io.offline)
-    BrowserOffline.toggleOfflineStatus();
-  Services.prefs.setIntPref('network.proxy.type', 0);
-  // LOAD_FLAGS_BYPASS_CACHE isn't good enough. So clear the cache.
-  Services.cache2.clear();
+  return toggleOfflineStatus(true);
 }
 
 function goOnline(callback) {
-  Services.prefs.setIntPref('network.proxy.type', origProxyType);
-  if (Services.io.offline)
-    BrowserOffline.toggleOfflineStatus();
-  if (callback)
-    callback();
+  return toggleOfflineStatus(false);
 }
 
 function openPanel(url, panelCallback, loadCallback) {
   // open a flyout
   SocialFlyout.open(url, 0, panelCallback);
-  SocialFlyout.panel.firstChild.addEventListener("load", function panelLoad(evt) {
-    if (evt.target != SocialFlyout.panel.firstChild.contentDocument) {
-      return;
-    }
-    SocialFlyout.panel.firstChild.removeEventListener("load", panelLoad, true);
-    loadCallback();
-  }, true);
+  // wait for both open and loaded before callback. Since the test doesn't close
+  // the panel between opens, we cannot rely on events here. We need to ensure
+  // popupshown happens before we finish out the tests.
+  waitForCondition(function() {
+                    return SocialFlyout.panel.state == "open" &&
+                           SocialFlyout.iframe.contentDocument.readyState == "complete";
+                   },
+                   function () { executeSoon(loadCallback) },
+                   "flyout is open and loaded");
 }
 
 function openChat(url, panelCallback, loadCallback) {
   // open a chat window
   let chatbar = getChatBar();
   openChatWindow(null, SocialSidebar.provider, url, panelCallback);
   chatbar.firstChild.addEventListener("DOMContentLoaded", function panelLoad() {
     chatbar.firstChild.removeEventListener("DOMContentLoaded", panelLoad, true);
-    loadCallback();
+    executeSoon(loadCallback);
   }, true);
 }
 
 function onSidebarLoad(callback) {
   let sbrowser = document.getElementById("social-sidebar-browser");
   sbrowser.addEventListener("load", function load() {
     sbrowser.removeEventListener("load", load, true);
-    callback();
+    executeSoon(callback);
   }, true);
 }
 
-function ensureWorkerLoaded(provider, callback) {
-  // once the worker responds to a ping we know it must be up.
-  let port = provider.getWorkerPort();
-  port.onmessage = function(msg) {
-    if (msg.data.topic == "pong") {
-      port.close();
-      callback();
-    }
-  }
-  port.postMessage({topic: "ping"})
-}
-
 let manifest = { // normal provider
   name: "provider 1",
   origin: "https://example.com",
-  sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
-  workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+  sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html",
   iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png"
 };
 
 function test() {
   waitForExplicitFinish();
 
   runSocialTestWithProvider(manifest, function (finishcb) {
-    runSocialTests(tests, undefined, goOnline, finishcb);
+    runSocialTests(tests, undefined, function(next) { goOnline().then(next) }, finishcb);
   });
 }
 
 var tests = {
   testSidebar: function(next) {
     let sbrowser = document.getElementById("social-sidebar-browser");
     onSidebarLoad(function() {
-      ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
+      ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "sidebar is on social error page");
       gc();
       // Add a new load listener, then find and click the "try again" button.
       onSidebarLoad(function() {
         // should still be on the error page.
-        ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "is still on social error page");
+        ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "sidebar is still on social error page");
         // go online and try again - this should work.
-        goOnline();
-        onSidebarLoad(function() {
-          // should now be on the correct page.
-          is(sbrowser.contentDocument.location.href, manifest.sidebarURL, "is now on social sidebar page");
-          next();
+        goOnline().then(function () {
+          onSidebarLoad(function() {
+            // should now be on the correct page.
+            is(sbrowser.contentDocument.location.href, manifest.sidebarURL, "sidebar is now on social sidebar page");
+            next();
+          });
+          sbrowser.contentDocument.getElementById("btnTryAgain").click();
         });
-        sbrowser.contentDocument.getElementById("btnTryAgain").click();
       });
       sbrowser.contentDocument.getElementById("btnTryAgain").click();
     });
-    // we want the worker to be fully loaded before going offline, otherwise
-    // it might fail due to going offline.
-    ensureWorkerLoaded(SocialSidebar.provider, function() {
-      // go offline then attempt to load the sidebar - it should fail.
-      goOffline();
+    // go offline then attempt to load the sidebar - it should fail.
+    goOffline().then(function() {
       SocialSidebar.show();
-  });
+    });
   },
 
   testFlyout: function(next) {
     let panelCallbackCount = 0;
     let panel = document.getElementById("social-flyout-panel");
-    // go offline and open a flyout.
-    goOffline();
-    openPanel(
-      "https://example.com/browser/browser/base/content/test/social/social_panel.html",
-      function() { // the panel api callback
-        panelCallbackCount++;
-      },
-      function() { // the "load" callback.
-        executeSoon(function() {
+    goOffline().then(function() {
+      openPanel(
+        manifest.sidebarURL, /* empty html page */
+        function() { // the panel api callback
+          panelCallbackCount++;
+        },
+        function() { // the "load" callback.
           todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
-          ok(panel.firstChild.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
+          let href = panel.firstChild.contentDocument.location.href;
+          ok(href.indexOf("about:socialerror?")==0, "flyout is on social error page");
           // Bug 832943 - the listeners previously stopped working after a GC, so
           // force a GC now and try again.
           gc();
           openPanel(
-            "https://example.com/browser/browser/base/content/test/social/social_panel.html",
+            manifest.sidebarURL, /* empty html page */
             function() { // the panel api callback
               panelCallbackCount++;
             },
             function() { // the "load" callback.
-              executeSoon(function() {
-                todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
-                ok(panel.firstChild.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page");
-                gc();
-                executeSoon(function() {
-                  SocialFlyout.unload();
-                  next();
-                });
-              });
+              todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
+              let href = panel.firstChild.contentDocument.location.href;
+              ok(href.indexOf("about:socialerror?")==0, "flyout is on social error page");
+              gc();
+              SocialFlyout.unload();
+              next();
             }
           );
-        });
-      }
-    );
+        }
+      );
+    });
   },
 
   testChatWindow: function(next) {
     let panelCallbackCount = 0;
-    // go offline and open a chat.
-    goOffline();
-    openChat(
-      "https://example.com/browser/browser/base/content/test/social/social_chat.html",
-      function() { // the panel api callback
-        panelCallbackCount++;
-      },
-      function() { // the "load" callback.
-        executeSoon(function() {
+    // chatwindow tests throw errors, which muddy test output, if the worker
+    // doesn't get test-init
+    goOffline().then(function() {
+      openChat(
+        manifest.sidebarURL, /* empty html page */
+        function() { // the panel api callback
+          panelCallbackCount++;
+        },
+        function() { // the "load" callback.
           todo_is(panelCallbackCount, 0, "Bug 833207 - should be no callback when error page loads.");
           let chat = getChatBar().selectedChat;
-          waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
+          waitForCondition(function() chat.content != null && chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
                            function() {
                             chat.close();
                             next();
                             },
                            "error page didn't appear");
-        });
-      }
-    );
+        }
+      );
+    });
   },
 
   testChatWindowAfterTearOff: function(next) {
     // Ensure that the error listener survives the chat window being detached.
-    let url = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
+    let url = manifest.sidebarURL; /* empty html page */
     let panelCallbackCount = 0;
+    // chatwindow tests throw errors, which muddy test output, if the worker
+    // doesn't get test-init
     // open a chat while we are still online.
     openChat(
       url,
       null,
       function() { // the "load" callback.
-        executeSoon(function() {
-          let chat = getChatBar().selectedChat;
-          is(chat.contentDocument.location.href, url, "correct url loaded");
-          // toggle to a detached window.
-          chat.swapWindows().then(
-            chat => {
+        let chat = getChatBar().selectedChat;
+        is(chat.contentDocument.location.href, url, "correct url loaded");
+        // toggle to a detached window.
+        chat.swapWindows().then(
+          chat => {
+            ok(!!chat.content, "we have chat content 1");
+            waitForCondition(function() chat.content != null && chat.contentDocument.readyState == "complete",
+                             function() {
               // now go offline and reload the chat - about:socialerror should be loaded.
-              goOffline();
-              chat.contentDocument.location.reload();
-              waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
-                               function() {
-                                chat.close();
-                                next();
-                                },
-                               "error page didn't appear");
-            }
-          );
-        });
+              goOffline().then(function() {
+                ok(!!chat.content, "we have chat content 2");
+                chat.contentDocument.location.reload();
+                info("chat reload called");
+                waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
+                                 function() {
+                                  chat.close();
+                                  next();
+                                  },
+                                 "error page didn't appear");
+              });
+            }, "swapped window loaded");
+          }
+        );
       }
     );
   }
 }
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -28,16 +28,26 @@ function waitForCondition(condition, nex
     if (conditionPassed) {
       moveOn();
     }
     tries++;
   }, 100);
   var moveOn = function() { clearInterval(interval); nextTest(); };
 }
 
+
+function promiseObserverNotified(aTopic) {
+  let deferred = Promise.defer();
+  Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) {
+    Services.obs.removeObserver(onNotification, aTopic);
+      deferred.resolve({subject: aSubject, data: aData});
+    }, aTopic, false);
+  return deferred.promise;
+}
+
 // Check that a specified (string) URL hasn't been "remembered" (ie, is not
 // in history, will not appear in about:newtab or auto-complete, etc.)
 function promiseSocialUrlNotRemembered(url) {
   let deferred = Promise.defer();
   let uri = Services.io.newURI(url, null, null);
   PlacesUtils.asyncHistory.isURIVisited(uri, function(aURI, aIsVisited) {
     ok(!aIsVisited, "social URL " + url + " should not be in global history");
     deferred.resolve();