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 220116 b36a41bb3c4433349a76db8e987af9abf93f9d9d
parent 220115 94902e0d21201d1950f0ea4cf5c771597562ca1d
child 220117 766d501a2066622975d149a296defb56f691c5c7
push id10442
push usermdeboer@mozilla.com
push dateWed, 17 Dec 2014 13:28:57 +0000
treeherderfx-team@b36a41bb3c44 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, dmose
bugs1080948
milestone37.0a1
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(() => {