Bug 1074678 - Open a room window when the user selects the room in the Loop panel. r=mikedeboer a=loop-only
authorMark Banner <standard8@mozilla.com>
Mon, 03 Nov 2014 16:34:03 +0000
changeset 235111 a279b0202fe445e1e1301f013d3096218cc8916f
parent 235110 fa172bfb0bfc1b5bc73996637f18e027c40c0ae9
child 235112 e0cd0e35ec15ab0676982c3e2f61cf488dd2a2c1
push id611
push userraliiev@mozilla.com
push dateMon, 05 Jan 2015 23:23:16 +0000
treeherdermozilla-release@345cd3b9c445 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikedeboer, loop-only
bugs1074678
milestone35.0a2
Bug 1074678 - Open a room window when the user selects the room in the Loop panel. r=mikedeboer a=loop-only
browser/components/loop/LoopRooms.jsm
browser/components/loop/content/js/conversationAppStore.js
browser/components/loop/content/js/panel.js
browser/components/loop/content/js/panel.jsx
browser/components/loop/content/shared/js/actions.js
browser/components/loop/content/shared/js/localRoomStore.js
browser/components/loop/content/shared/js/roomListStore.js
browser/components/loop/test/desktop-local/conversationAppStore_test.js
browser/components/loop/test/desktop-local/panel_test.js
browser/components/loop/test/shared/localRoomStore_test.js
browser/components/loop/test/shared/roomListStore_test.js
browser/components/loop/test/xpcshell/test_looprooms.js
--- a/browser/components/loop/LoopRooms.jsm
+++ b/browser/components/loop/LoopRooms.jsm
@@ -161,16 +161,25 @@ let LoopRoomsInternal = {
         delete room.expiresIn;
         this.rooms.set(room.roomToken, room);
 
         eventEmitter.emit("add", room);
         callback(null, room);
       }, error => callback(error)).catch(error => callback(error));
   },
 
+  open: function(roomToken) {
+    let windowData = {
+      roomToken: roomToken,
+      type: "room"
+    };
+
+    MozLoopService.openChatWindow(windowData);
+  },
+
   /**
    * Callback used to indicate changes to rooms data on the LoopServer.
    *
    * @param {String} version   Version number assigned to this change set.
    * @param {String} channelID Notification channel identifier.
    */
   onNotification: function(version, channelID) {
     gDirty = true;
@@ -200,16 +209,20 @@ this.LoopRooms = {
   get: function(roomToken, callback) {
     return LoopRoomsInternal.get(roomToken, callback);
   },
 
   create: function(options, callback) {
     return LoopRoomsInternal.create(options, callback);
   },
 
+  open: function(roomToken) {
+    return LoopRoomsInternal.open(roomToken);
+  },
+
   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/content/js/conversationAppStore.js
+++ b/browser/components/loop/content/js/conversationAppStore.js
@@ -56,23 +56,17 @@ loop.store.ConversationAppStore = (funct
 
     /**
      * Handles the get window data action - obtains the window data,
      * updates the store and notifies interested components.
      *
      * @param {sharedActions.GetWindowData} actionData The action data
      */
     getWindowData: function(actionData) {
-      var windowData;
-      // XXX Remove me in bug 1074678
-      if (this._mozLoop.getLoopBoolPref("test.alwaysUseRooms")) {
-        windowData = {type: "room", localRoomId: "42"};
-      } else {
-        windowData = this._mozLoop.getConversationWindowData(actionData.windowId);
-      }
+      var windowData = this._mozLoop.getConversationWindowData(actionData.windowId);
 
       if (!windowData) {
         console.error("Failed to get the window data");
         this.setStoreState({windowType: "failed"});
         return;
       }
 
       // XXX windowData is a hack for the IncomingConversationView until
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -577,17 +577,19 @@ loop.panel = (function(_, mozL10n) {
     handleCreateButtonClick: function() {
       this.props.dispatcher.dispatch(new sharedActions.CreateRoom({
         nameTemplate: mozL10n.get("rooms_default_room_name_template"),
         roomOwner: this.props.userDisplayName
       }));
     },
 
     openRoom: function(room) {
-      // XXX implement me; see bug 1074678
+      this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
+        roomToken: room.roomToken
+      }));
     },
 
     render: function() {
       if (this.state.error) {
         // XXX Better end user reporting of errors.
         console.error("RoomList error", this.state.error);
       }
 
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -577,17 +577,19 @@ loop.panel = (function(_, mozL10n) {
     handleCreateButtonClick: function() {
       this.props.dispatcher.dispatch(new sharedActions.CreateRoom({
         nameTemplate: mozL10n.get("rooms_default_room_name_template"),
         roomOwner: this.props.userDisplayName
       }));
     },
 
     openRoom: function(room) {
-      // XXX implement me; see bug 1074678
+      this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
+        roomToken: room.roomToken
+      }));
     },
 
     render: function() {
       if (this.state.error) {
         // XXX Better end user reporting of errors.
         console.error("RoomList error", this.state.error);
       }
 
--- a/browser/components/loop/content/shared/js/actions.js
+++ b/browser/components/loop/content/shared/js/actions.js
@@ -174,11 +174,19 @@ loop.shared.actions = (function() {
     }),
 
     /**
      * Updates room list.
      * XXX: should move to some roomActions module - refs bug 1079284
      */
     UpdateRoomList: Action.define("updateRoomList", {
       roomList: Array
+    }),
+
+    /**
+     * Opens a room.
+     * XXX: should move to some roomActions module - refs bug 1079284
+     */
+    OpenRoom: Action.define("openRoom", {
+      roomToken: String
     })
   };
 })();
--- a/browser/components/loop/content/shared/js/localRoomStore.js
+++ b/browser/components/loop/content/shared/js/localRoomStore.js
@@ -66,55 +66,38 @@ loop.store.LocalRoomStore = (function() 
     },
 
     setStoreState: function(state) {
       this._storeState = state;
       this.trigger("change");
     },
 
     /**
-     * Proxy to mozLoop.rooms.getRoomData for setupEmptyRoom action.
-     *
-     * XXXremoveMe Can probably be removed when bug 1074664 lands.
-     *
-     * @param {Integer} roomId The id of the room.
-     * @param {Function} cb Callback(error, roomData)
-     */
-    _fetchRoomData: function(roomId, cb) {
-      // XXX Remove me in bug 1074678
-      if (!this.mozLoop.getLoopBoolPref("test.alwaysUseRooms")) {
-        this.mozLoop.rooms.getRoomData(roomId, cb);
-      } else {
-        cb(null, {roomName: "Donkeys"});
-      }
-    },
-
-    /**
-     * Execute setupEmptyRoom event action from the dispatcher.  This primes
-     * the store with the localRoomId, and calls MozLoop.getRoomData on that
+     * Execute setupWindowData event action from the dispatcher.  This primes
+     * the store with the roomToken, and calls MozLoop.getRoomData on that
      * ID.  This will return either a reflection of state on the server, or,
      * if the createRoom call hasn't yet returned, it will have at least the
      * roomName as specified to the createRoom method.
      *
      * When the room name gets set, that will trigger the view to display
      * that name.
      *
      * @param {sharedActions.SetupWindowData} actionData
      */
     setupWindowData: function(actionData) {
       if (actionData.type !== "room") {
         // Nothing for us to do here, leave it to other stores.
         return;
       }
 
-      this._fetchRoomData(actionData.localRoomId,
+      this.mozLoop.rooms.get(actionData.roomToken,
         function(error, roomData) {
           this.setStoreState({
             error: error,
-            localRoomId: actionData.localRoomId,
+            roomToken: actionData.roomToken,
             serverData: roomData
           });
         }.bind(this));
     }
 
   }, Backbone.Events);
 
   return LocalRoomStore;
--- a/browser/components/loop/content/shared/js/roomListStore.js
+++ b/browser/components/loop/content/shared/js/roomListStore.js
@@ -323,12 +323,21 @@ loop.store = loop.store || {};
      * @param {sharedActions.UpdateRoomList} actionData The action data.
      */
     updateRoomList: function(actionData) {
       this.setStoreState({
         error: undefined,
         rooms: this._processRoomList(actionData.roomList)
       });
     },
+
+    /**
+     * Opens a room
+     *
+     * @param {sharedActions.OpenRoom} actionData The action data.
+     */
+    openRoom: function(actionData) {
+      this._mozLoop.rooms.open(actionData.roomToken);
+    }
   }, Backbone.Events);
 
   loop.store.RoomListStore = RoomListStore;
 })();
--- a/browser/components/loop/test/desktop-local/conversationAppStore_test.js
+++ b/browser/components/loop/test/desktop-local/conversationAppStore_test.js
@@ -40,18 +40,16 @@ describe("loop.store.ConversationAppStor
         callId: "123456"
       };
 
       fakeGetWindowData = {
         windowId: "42"
       };
 
       fakeMozLoop = {
-        // XXX Remove me in bug 1074678
-        getLoopBoolPref: function() { return false; },
         getConversationWindowData: function(windowId) {
           if (windowId === "42") {
             return fakeWindowData;
           }
           return null;
         }
       };
 
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -758,27 +758,41 @@ describe("loop.panel", function() {
       function() {
         var dispatch = sandbox.stub(dispatcher, "dispatch");
         roomListStore.setStoreState({pendingCreation: true});
 
         var view = createTestComponent();
 
         var buttonNode = view.getDOMNode().querySelector("button[disabled]");
         expect(buttonNode).to.not.equal(null);
-    });
+      });
 
     it("should disable the create button when a list retrieval operation is pending",
       function() {
         var dispatch = sandbox.stub(dispatcher, "dispatch");
         roomListStore.setStoreState({pendingInitialRetrieval: true});
 
         var view = createTestComponent();
 
         var buttonNode = view.getDOMNode().querySelector("button[disabled]");
         expect(buttonNode).to.not.equal(null);
+      });
+
+    describe("#openRoom", function() {
+      it("should dispatch an OpenRoom action", function() {
+        var view = createTestComponent();
+        var dispatch = sandbox.stub(dispatcher, "dispatch");
+
+        view.openRoom({roomToken: "42cba"});
+
+        sinon.assert.calledOnce(dispatch);
+        sinon.assert.calledWithExactly(dispatch, new sharedActions.OpenRoom({
+          roomToken: "42cba"
+        }));
+      });
     });
   });
 
   describe('loop.panel.ToSView', function() {
 
     it("should render when the value of loop.seenToS is not set", function() {
       var view = TestUtils.renderIntoDocument(loop.panel.ToSView());
 
--- a/browser/components/loop/test/shared/localRoomStore_test.js
+++ b/browser/components/loop/test/shared/localRoomStore_test.js
@@ -27,97 +27,96 @@ describe("loop.store.LocalRoomStore", fu
     it("should throw an error if mozLoop is missing", function() {
       expect(function() {
         new loop.store.LocalRoomStore({dispatcher: dispatcher});
       }).to.Throw(/mozLoop/);
     });
   });
 
   describe("#setupWindowData", function() {
-    var store, fakeMozLoop, fakeRoomId, fakeRoomName;
+    var store, fakeMozLoop, fakeToken, fakeRoomName;
 
     beforeEach(function() {
-      fakeRoomId = "337-ff-54";
+      fakeToken = "337-ff-54";
       fakeRoomName = "Monkeys";
       fakeMozLoop = {
-        rooms: { getRoomData: sandbox.stub() },
-        getLoopBoolPref: function () { return false; }
+        rooms: { get: sandbox.stub() }
       };
 
       store = new loop.store.LocalRoomStore(
         {mozLoop: fakeMozLoop, dispatcher: dispatcher});
-      fakeMozLoop.rooms.getRoomData.
-        withArgs(fakeRoomId).
+      fakeMozLoop.rooms.get.
+        withArgs(fakeToken).
         callsArgOnWith(1, // index of callback argument
         store, // |this| to call it on
         null, // args to call the callback with...
         {roomName: fakeRoomName}
       );
     });
 
     it("should trigger a change event", function(done) {
       store.on("change", function() {
         done();
       });
 
       dispatcher.dispatch(new sharedActions.SetupWindowData({
         windowId: "42",
         type: "room",
-        localRoomId: fakeRoomId
+        roomToken: fakeToken
       }));
     });
 
     it("should set localRoomId on the store from the action data",
       function(done) {
 
         store.once("change", function () {
           expect(store.getStoreState()).
-            to.have.property('localRoomId', fakeRoomId);
+            to.have.property('roomToken', fakeToken);
           done();
         });
 
         dispatcher.dispatch(new sharedActions.SetupWindowData({
           windowId: "42",
           type: "room",
-          localRoomId: fakeRoomId
+          roomToken: fakeToken
         }));
       });
 
     it("should set serverData.roomName from the getRoomData callback",
       function(done) {
 
         store.once("change", function () {
           expect(store.getStoreState()).to.have.deep.property(
             'serverData.roomName', fakeRoomName);
           done();
         });
 
         dispatcher.dispatch(new sharedActions.SetupWindowData({
           windowId: "42",
           type: "room",
-          localRoomId: fakeRoomId
+          roomToken: fakeToken
         }));
       });
 
     it("should set error on the store when getRoomData calls back an error",
       function(done) {
 
         var fakeError = new Error("fake error");
-        fakeMozLoop.rooms.getRoomData.
-          withArgs(fakeRoomId).
+        fakeMozLoop.rooms.get.
+          withArgs(fakeToken).
           callsArgOnWith(1, // index of callback argument
           store, // |this| to call it on
           fakeError); // args to call the callback with...
 
         store.once("change", function() {
           expect(this.getStoreState()).to.have.property('error', fakeError);
           done();
         });
 
         dispatcher.dispatch(new sharedActions.SetupWindowData({
           windowId: "42",
           type: "room",
-          localRoomId: fakeRoomId
+          roomToken: fakeToken
         }));
       });
 
   });
 });
--- a/browser/components/loop/test/shared/roomListStore_test.js
+++ b/browser/components/loop/test/shared/roomListStore_test.js
@@ -321,9 +321,32 @@ describe("loop.store.RoomListStore", fun
         };
 
         store.getAllRooms();
 
         expect(store.getStoreState().pendingInitialRetrieval).eql(false);
       });
     });
   });
+
+  describe("#openRoom", function() {
+    var store, fakeMozLoop;
+
+    beforeEach(function() {
+      fakeMozLoop = {
+        rooms: {
+          open: sinon.spy()
+        }
+      };
+      store = new loop.store.RoomListStore({
+        dispatcher: dispatcher,
+        mozLoop: fakeMozLoop
+      });
+    });
+
+    it("should open the room via mozLoop", function() {
+      dispatcher.dispatch(new sharedActions.OpenRoom({roomToken: "42abc"}));
+
+      sinon.assert.calledOnce(fakeMozLoop.rooms.open);
+      sinon.assert.calledWithExactly(fakeMozLoop.rooms.open, "42abc");
+    });
+  });
 });
--- a/browser/components/loop/test/xpcshell/test_looprooms.js
+++ b/browser/components/loop/test/xpcshell/test_looprooms.js
@@ -1,14 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource:///modules/loop/LoopRooms.jsm");
+Cu.import("resource:///modules/Chat.jsm");
+
+let openChatOrig = Chat.open;
 
 const kRooms = new Map([
   ["_nxD4V4FflQ", {
     roomToken: "_nxD4V4FflQ",
     roomName: "First Room Name",
     roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ",
     maxSize: 2,
     currSize: 0,
@@ -163,13 +166,36 @@ add_task(function* test_createRoom() {
     compareRooms(room, kCreateRoomProps);
     eventCalled = true;
   });
   let room = yield LoopRooms.promise("create", kCreateRoomProps);
   compareRooms(room, kCreateRoomProps);
   Assert.ok(eventCalled, "Event should have fired");
 });
 
+add_task(function* test_openRoom() {
+  let openedUrl;
+  Chat.open = function(contentWindow, origin, title, url) {
+    openedUrl = url;
+  };
+
+  LoopRooms.open("fakeToken");
+
+  Assert.ok(openedUrl, "should open a chat window");
+
+  // Stop the busy kicking in for following tests.
+  let windowId = openedUrl.match(/about:loopconversation\#(\d+)$/)[1];
+  let windowData = MozLoopService.getConversationWindowData(windowId);
+
+  Assert.equal(windowData.type, "room", "window data should contain room as the type");
+  Assert.equal(windowData.roomToken, "fakeToken", "window data should have the roomToken");
+});
+
 function run_test() {
+  do_register_cleanup(function() {
+    // Revert original Chat.open implementation
+    Chat.open = openChatOrig;
+  });
+
   setupFakeLoopServer();
 
   run_next_test();
 }