Bug 1080948: UITour: tell the page when a URL is copied or emailed. r=MattN,dmose
authorMike de Boer <mdeboer@mozilla.com>
Wed, 17 Dec 2014 09:33:08 +0100
changeset 220244 b36a41bb3c4433349a76db8e987af9abf93f9d9d
parent 220243 94902e0d21201d1950f0ea4cf5c771597562ca1d
child 220245 766d501a2066622975d149a296defb56f691c5c7
push id53051
push userryanvm@gmail.com
push dateThu, 18 Dec 2014 02:08:11 +0000
treeherdermozilla-inbound@ffb2b4550976 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, dmose
bugs1080948
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1080948: UITour: tell the page when a URL is copied or emailed. r=MattN,dmose
browser/components/loop/LoopRooms.jsm
browser/components/loop/MozLoopAPI.jsm
browser/components/loop/content/shared/js/roomStore.js
browser/modules/test/browser_UITour_loop.js
--- a/browser/components/loop/LoopRooms.jsm
+++ b/browser/components/loop/LoopRooms.jsm
@@ -565,16 +565,37 @@ this.LoopRooms = {
   getGuestCreatedRoom: function() {
     return LoopRoomsInternal.getGuestCreatedRoom();
   },
 
   maybeRefresh: function(user) {
     return LoopRoomsInternal.maybeRefresh(user);
   },
 
+  /**
+   * This method is only useful for unit tests to set the rooms cache to contain
+   * a list of fake room data that can be asserted in tests.
+   *
+   * @param {Map} stub Stub cache containing fake rooms data
+   */
+  stubCache: function(stub) {
+    LoopRoomsInternal.rooms.clear();
+    if (stub) {
+      // Fill up the rooms cache with room objects provided in the `stub` Map.
+      for (let [key, value] of stub.entries()) {
+        LoopRoomsInternal.rooms.set(key, value);
+      }
+      gDirty = false;
+    } else {
+      // Restore the cache to not be stubbed anymore, but it'll need a refresh
+      // from the server for sure.
+      gDirty = true;
+    }
+  },
+
   promise: function(method, ...params) {
     return new Promise((resolve, reject) => {
       this[method](...params, (error, result) => {
         if (error) {
           reject(error);
         } else {
           resolve(result);
         }
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -18,16 +18,18 @@ Cu.importGlobalProperties(["Blob"]);
 XPCOMUtils.defineLazyModuleGetter(this, "LoopContacts",
                                         "resource:///modules/loop/LoopContacts.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
                                         "resource:///modules/loop/LoopStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "hookWindowCloseForPanelClose",
                                         "resource://gre/modules/MozSocialAPI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                         "resource://gre/modules/PluralForm.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UITour",
+                                        "resource:///modules/UITour.jsm");
 XPCOMUtils.defineLazyGetter(this, "appInfo", function() {
   return Cc["@mozilla.org/xre/app-info;1"]
            .getService(Ci.nsIXULAppInfo)
            .QueryInterface(Ci.nsIXULRuntime);
 });
 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
                                          "@mozilla.org/widget/clipboardhelper;1",
                                          "nsIClipboardHelper");
@@ -730,17 +732,31 @@ function injectLoopAPI(targetWindow) {
       enumerable: true,
       writable: true,
       value: function(windowId, sessionId, callid) {
         MozLoopService.addConversationContext(windowId, {
           sessionId: sessionId,
           callId: callid
         });
       }
-    }
+    },
+
+    /**
+     * Notifies the UITour module that an event occurred that it might be
+     * interested in.
+     *
+     * @param {String} subject Subject of the notification
+     */
+    notifyUITour: {
+      enumerable: true,
+      writable: true,
+      value: function(subject) {
+        UITour.notify(subject);
+      }
+    },
   };
 
   function onStatusChanged(aSubject, aTopic, aData) {
     let event = new targetWindow.CustomEvent("LoopStatusChanged");
     targetWindow.dispatchEvent(event);
   };
 
   function onDOMWindowDestroyed(aSubject, aTopic, aData) {
--- a/browser/components/loop/content/shared/js/roomStore.js
+++ b/browser/components/loop/content/shared/js/roomStore.js
@@ -293,25 +293,27 @@ loop.store = loop.store || {};
 
     /**
      * Copy a room url.
      *
      * @param  {sharedActions.CopyRoomUrl} actionData The action data.
      */
     copyRoomUrl: function(actionData) {
       this._mozLoop.copyString(actionData.roomUrl);
+      this._mozLoop.notifyUITour("Loop:RoomURLCopied");
     },
 
     /**
      * Emails a room url.
      *
      * @param  {sharedActions.EmailRoomUrl} actionData The action data.
      */
     emailRoomUrl: function(actionData) {
       loop.shared.utils.composeCallUrlEmail(actionData.roomUrl);
+      this._mozLoop.notifyUITour("Loop:RoomURLEmailed");
     },
 
     /**
      * Creates a new room.
      *
      * @param {sharedActions.DeleteRoom} actionData The action data.
      */
     deleteRoom: function(actionData) {
--- a/browser/modules/test/browser_UITour_loop.js
+++ b/browser/modules/test/browser_UITour_loop.js
@@ -98,16 +98,68 @@ let tests = [
           });
         });
         done();
       });
       document.querySelector("#pinnedchats > chatbox").close();
     });
     LoopRooms.open("fakeTourRoom");
   },
+  function test_notifyLoopRoomURLCopied(done) {
+    gContentAPI.observe((event, params) => {
+      is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
+      gContentAPI.observe((event, params) => {
+        is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
+
+        let chat = document.querySelector("#pinnedchats > chatbox");
+        gContentAPI.observe((event, params) => {
+          is(event, "Loop:RoomURLCopied", "Check Loop:RoomURLCopied notification");
+          gContentAPI.observe((event, params) => {
+            is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
+          });
+          chat.close();
+          done();
+        });
+        chat.content.contentDocument.querySelector(".btn-copy").click();
+      });
+    });
+    setupFakeRoom();
+    LoopRooms.open("fakeTourRoom");
+  },
+  function test_notifyLoopRoomURLEmailed(done) {
+    gContentAPI.observe((event, params) => {
+      is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
+      gContentAPI.observe((event, params) => {
+        is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
+
+        let chat = document.querySelector("#pinnedchats > chatbox");
+        let composeEmailCalled = false;
+
+        gContentAPI.observe((event, params) => {
+          is(event, "Loop:RoomURLEmailed", "Check Loop:RoomURLEmailed notification");
+          ok(composeEmailCalled, "mozLoop.composeEmail should be called");
+          gContentAPI.observe((event, params) => {
+            is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
+          });
+          chat.close();
+          done();
+        });
+
+        let chatWin = chat.content.contentWindow;
+        let oldComposeEmail = chatWin.navigator.wrappedJSObject.mozLoop.composeEmail;
+        chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = function(recipient, subject, body) {
+          ok(recipient, "composeEmail should be invoked with at least a recipient value");
+          composeEmailCalled = true;
+          chatWin.navigator.wrappedJSObject.mozLoop.composeEmail = oldComposeEmail;
+        };
+        chatWin.document.querySelector(".btn-email").click();
+      });
+    });
+    LoopRooms.open("fakeTourRoom");
+  },
   taskify(function* test_arrow_panel_position() {
     ise(loopButton.open, false, "Menu should initially be closed");
     let popup = document.getElementById("UITourTooltip");
 
     yield showMenuPromise("loop");
 
     let currentTarget = "loop-newRoom";
     yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
@@ -127,16 +179,25 @@ let tests = [
 
 function checkLoopPanelIsHidden() {
   ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
   ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
   isnot(loopPanel.state, "open", "The panel shouldn't be open");
   is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
 }
 
+function setupFakeRoom() {
+  let room = {};
+  for (let prop of ["roomToken", "roomName", "roomOwner", "roomUrl", "participants"])
+    room[prop] = "fakeTourRoom";
+  LoopRooms.stubCache(new Map([
+    [room.roomToken, room]
+  ]));
+}
+
 if (Services.prefs.getBoolPref("loop.enabled")) {
   loopButton = window.LoopUI.toolbarButton.node;
   // The targets to highlight only appear after getting started is launched.
   Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
   Services.prefs.setCharPref("loop.server", "http://localhost");
   Services.prefs.setCharPref("services.push.serverURL", "ws://localhost/");
 
   registerCleanupFunction(() => {