Bug 1132301: Part 5 - add unit tests for the Social Sharing feature of Loop. r=Standard8
☠☠ backed out by 3f3bb18b9f04 ☠ ☠
authorMike de Boer <mdeboer@mozilla.com>
Fri, 27 Mar 2015 14:32:01 +0100
changeset 264992 ef364246350a5f0af2b57fcfac7fb23188e8384e
parent 264991 0e57fb772be5ffe2628824d30fc562a4cf75e571
child 264993 697c11bd51f6ad213e375efa2e1fd2c253f98544
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersStandard8
bugs1132301
milestone39.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 1132301: Part 5 - add unit tests for the Social Sharing feature of Loop. r=Standard8
browser/components/loop/test/desktop-local/panel_test.js
browser/components/loop/test/desktop-local/roomViews_test.js
browser/components/loop/test/shared/activeRoomStore_test.js
browser/components/loop/test/shared/roomStore_test.js
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -24,17 +24,18 @@ describe("loop.panel", function() {
     // https://github.com/cjohansen/Sinon.JS/issues/393
     fakeXHR.xhr.onCreate = function (xhr) {
       requests.push(xhr);
     };
 
     fakeWindow = {
       close: sandbox.stub(),
       addEventListener: function() {},
-      document: { addEventListener: function(){} }
+      document: { addEventListener: function(){} },
+      setTimeout: function(callback) { callback(); }
     };
     loop.shared.mixins.setRootObject(fakeWindow);
 
     notifications = new loop.shared.models.NotificationCollection();
 
     fakeMozLoop = navigator.mozLoop = {
       doNotDisturb: true,
       fxAEnabled: true,
--- a/browser/components/loop/test/desktop-local/roomViews_test.js
+++ b/browser/components/loop/test/desktop-local/roomViews_test.js
@@ -13,27 +13,29 @@ describe("loop.roomViews", function () {
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
 
     dispatcher = new loop.Dispatcher();
 
     fakeMozLoop = {
       getAudioBlob: sinon.stub(),
-      getLoopPref: sinon.stub()
+      getLoopPref: sinon.stub(),
+      isSocialShareButtonAvailable: sinon.stub()
     };
 
     fakeWindow = {
       close: sinon.stub(),
       document: {},
       navigator: {
         mozLoop: fakeMozLoop
       },
       addEventListener: function() {},
-      removeEventListener: function() {}
+      removeEventListener: function() {},
+      setTimeout: function(callback) { callback(); }
     };
     loop.shared.mixins.setRootObject(fakeWindow);
 
     // XXX These stubs should be hoisted in a common file
     // Bug 1040968
     sandbox.stub(document.mozL10n, "get", function(x) {
       return x;
     });
@@ -193,16 +195,31 @@ describe("loop.roomViews", function () {
           var copyBtn = view.getDOMNode().querySelector('.btn-copy');
 
           React.addons.TestUtils.Simulate.click(copyBtn);
 
           // copied_url_button is the l10n string.
           expect(copyBtn.textContent).eql("copied_url_button");
       });
     });
+
+    describe("Share button", function() {
+      beforeEach(function() {
+        view = mountTestComponent();
+      });
+
+      it("should toggle the share dropdown when the share button is clicked", function() {
+        var shareBtn = view.getDOMNode().querySelector(".btn-share");
+
+        React.addons.TestUtils.Simulate.click(shareBtn);
+
+        expect(view.state.showMenu).to.eql(true);
+        expect(view.refs.menu.props.show).to.eql(true);
+      });
+    });
   });
 
   describe("DesktopRoomConversationView", function() {
     var view;
 
     beforeEach(function() {
       loop.store.StoreMixin.register({
         feedbackStore: new loop.store.FeedbackStore(dispatcher, {
@@ -428,9 +445,145 @@ describe("loop.roomViews", function () {
 
           view = mountTestComponent();
 
           expect(view.getDOMNode().querySelector(".local-stream-audio"))
             .not.eql(null);
         });
     });
   });
+
+  describe("SocialShareDropdown", function() {
+    var view, fakeProvider;
+
+    beforeEach(function() {
+      sandbox.stub(dispatcher, "dispatch");
+
+      fakeProvider = {
+        name: "foo",
+        origin: "https://foo",
+        iconURL: "http://example.com/foo.png"
+      };
+    });
+
+    afterEach(function() {
+      view = fakeProvider = null;
+    });
+
+    function mountTestComponent() {
+      return TestUtils.renderIntoDocument(
+        React.createElement(loop.roomViews.SocialShareDropdown, {
+          dispatcher: dispatcher,
+          roomStore: roomStore,
+          show: true
+        })
+      );
+    }
+
+    describe("#render", function() {
+      it("should show no contents when the Social Providers have not been fetched yet", function() {
+        view = mountTestComponent();
+
+        expect(view.getDOMNode()).to.eql(null);
+      });
+
+      it("should show different contents when the Share XUL button is not available", function() {
+        activeRoomStore.setStoreState({
+          socialShareProviders: []
+        });
+        view = mountTestComponent();
+
+        var node = view.getDOMNode();
+        expect(node.querySelector(".share-panel-header")).to.not.eql(null);
+      });
+
+      it("should show an empty list when no Social Providers are available", function() {
+        activeRoomStore.setStoreState({
+          socialShareButtonAvailable: true,
+          socialShareProviders: []
+        });
+
+        view = mountTestComponent();
+
+        var node = view.getDOMNode();
+        expect(node.querySelector(".icon-add-share-service")).to.not.eql(null);
+        expect(node.querySelectorAll(".dropdown-menu-item").length).to.eql(1);
+      });
+
+      it("should show a list of available Social Providers", function() {
+        activeRoomStore.setStoreState({
+          socialShareButtonAvailable: true,
+          socialShareProviders: [fakeProvider]
+        });
+
+        view = mountTestComponent();
+
+        var node = view.getDOMNode();
+        expect(node.querySelector(".icon-add-share-service")).to.not.eql(null);
+        expect(node.querySelector(".dropdown-menu-separator")).to.not.eql(null);
+
+        var dropdownNodes = node.querySelectorAll(".dropdown-menu-item");
+        expect(dropdownNodes.length).to.eql(2);
+        expect(dropdownNodes[1].querySelector("img").src).to.eql(fakeProvider.iconURL);
+        expect(dropdownNodes[1].querySelector("span").textContent)
+          .to.eql(fakeProvider.name);
+      });
+    });
+
+    describe("#handleToolbarAddButtonClick", function() {
+      it("should dispatch an action when the 'add to toolbar' button is clicked", function() {
+        activeRoomStore.setStoreState({
+          socialShareProviders: []
+        });
+
+        view = mountTestComponent();
+
+        var addButton = view.getDOMNode().querySelector(".btn-toolbar-add");
+        React.addons.TestUtils.Simulate.click(addButton);
+
+        sinon.assert.calledOnce(dispatcher.dispatch);
+        sinon.assert.calledWithExactly(dispatcher.dispatch,
+          new sharedActions.AddSocialShareButton());
+      });
+    });
+
+    describe("#handleAddServiceClick", function() {
+      it("should dispatch an action when the 'add provider' item is clicked", function() {
+        activeRoomStore.setStoreState({
+          socialShareProviders: [],
+          socialShareButtonAvailable: true
+        });
+
+        view = mountTestComponent();
+
+        var addItem = view.getDOMNode().querySelector(".dropdown-menu-item:first-child");
+        React.addons.TestUtils.Simulate.click(addItem);
+
+        sinon.assert.calledOnce(dispatcher.dispatch);
+        sinon.assert.calledWithExactly(dispatcher.dispatch,
+          new sharedActions.AddSocialShareProvider());
+      });
+    });
+
+    describe("#handleProviderClick", function() {
+      it("should dispatch an action when a provider item is clicked", function() {
+        activeRoomStore.setStoreState({
+          roomUrl: "http://example.com",
+          socialShareButtonAvailable: true,
+          socialShareProviders: [fakeProvider]
+        });
+
+        view = mountTestComponent();
+
+        var providerItem = view.getDOMNode().querySelector(".dropdown-menu-item:last-child");
+        React.addons.TestUtils.Simulate.click(providerItem);
+
+        sinon.assert.calledOnce(dispatcher.dispatch);
+        sinon.assert.calledWithExactly(dispatcher.dispatch,
+          new sharedActions.ShareRoomUrl({
+            provider: fakeProvider,
+            roomUrl: activeRoomStore.getStoreState().roomUrl,
+            previews: []
+          }));
+      });
+    });
+  });
 });
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -29,17 +29,19 @@ describe("loop.store.ActiveRoomStore", f
         get: sinon.stub(),
         join: sinon.stub(),
         refreshMembership: sinon.stub(),
         leave: sinon.stub(),
         on: sinon.stub(),
         off: sinon.stub()
       },
       setScreenShareState: sinon.stub(),
-      getActiveTabWindowId: sandbox.stub().callsArgWith(0, null, 42)
+      getActiveTabWindowId: sandbox.stub().callsArgWith(0, null, 42),
+      isSocialShareButtonAvailable: sinon.stub().returns(false),
+      getSocialShareProviders: sinon.stub().returns([])
     };
 
     fakeSdkDriver = {
       connectSession: sinon.stub(),
       disconnectSession: sinon.stub(),
       forceDisconnectAll: sinon.stub().callsArg(0),
       retryPublishWithoutVideo: sinon.stub(),
       startScreenShare: sinon.stub(),
@@ -272,17 +274,19 @@ describe("loop.store.ActiveRoomStore", f
           windowId: "42",
           type: "room",
           roomToken: fakeToken
         }));
 
         sinon.assert.calledTwice(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.SetupRoomInfo(_.extend({
-            roomToken: fakeToken
+            roomToken: fakeToken,
+            socialShareButtonAvailable: false,
+            socialShareProviders: []
           }, fakeRoomData)));
       });
 
     it("should dispatch a JoinRoom action if the get is successful",
       function() {
         store.setupWindowData(new sharedActions.SetupWindowData({
           windowId: "42",
           type: "room",
@@ -408,17 +412,19 @@ describe("loop.store.ActiveRoomStore", f
   describe("#setupRoomInfo", function() {
     var fakeRoomInfo;
 
     beforeEach(function() {
       fakeRoomInfo = {
         roomName: "Its a room",
         roomOwner: "Me",
         roomToken: "fakeToken",
-        roomUrl: "http://invalid"
+        roomUrl: "http://invalid",
+        socialShareButtonAvailable: false,
+        socialShareProviders: []
       };
     });
 
     it("should set the state to READY", function() {
       store.setupRoomInfo(new sharedActions.SetupRoomInfo(fakeRoomInfo));
 
       expect(store._storeState.roomState).eql(ROOM_STATES.READY);
     });
@@ -426,16 +432,18 @@ describe("loop.store.ActiveRoomStore", f
     it("should save the room information", function() {
       store.setupRoomInfo(new sharedActions.SetupRoomInfo(fakeRoomInfo));
 
       var state = store.getStoreState();
       expect(state.roomName).eql(fakeRoomInfo.roomName);
       expect(state.roomOwner).eql(fakeRoomInfo.roomOwner);
       expect(state.roomToken).eql(fakeRoomInfo.roomToken);
       expect(state.roomUrl).eql(fakeRoomInfo.roomUrl);
+      expect(state.socialShareButtonAvailable).eql(false);
+      expect(state.socialShareProviders).eql([]);
     });
   });
 
   describe("#updateRoomInfo", function() {
     var fakeRoomInfo;
 
     beforeEach(function() {
       fakeRoomInfo = {
@@ -450,16 +458,41 @@ describe("loop.store.ActiveRoomStore", f
 
       var state = store.getStoreState();
       expect(state.roomName).eql(fakeRoomInfo.roomName);
       expect(state.roomOwner).eql(fakeRoomInfo.roomOwner);
       expect(state.roomUrl).eql(fakeRoomInfo.roomUrl);
     });
   });
 
+  describe("#updateSocialShareInfo", function() {
+    var fakeSocialShareInfo;
+
+    beforeEach(function() {
+      fakeSocialShareInfo = {
+        socialShareButtonAvailable: true,
+        socialShareProviders: [{
+          name: "foo",
+          origin: "https://example.com",
+          iconURL: "icon.png"
+        }]
+      };
+    });
+
+    it("should save the Social API information", function() {
+      store.updateSocialShareInfo(new sharedActions.UpdateSocialShareInfo(fakeSocialShareInfo));
+
+      var state = store.getStoreState();
+      expect(state.socialShareButtonAvailable)
+        .eql(fakeSocialShareInfo.socialShareButtonAvailable);
+      expect(state.socialShareProviders)
+        .eql(fakeSocialShareInfo.socialShareProviders);
+    });
+  });
+
   describe("#joinRoom", function() {
     it("should reset failureReason", function() {
       store.setStoreState({failureReason: "Test"});
 
       store.joinRoom();
 
       expect(store.getStoreState().failureReason).eql(undefined);
     });
@@ -1089,24 +1122,46 @@ describe("loop.store.ActiveRoomStore", f
 
     it("should set the state to ENDED", function() {
       store.leaveRoom();
 
       expect(store._storeState.roomState).eql(ROOM_STATES.ENDED);
     });
   });
 
+  describe("#_handleSocialShareUpdate", function() {
+    it("should dispatch an UpdateRoomInfo action", function() {
+      store._handleSocialShareUpdate();
+
+      sinon.assert.calledOnce(dispatcher.dispatch);
+      sinon.assert.calledWithExactly(dispatcher.dispatch,
+        new sharedActions.UpdateSocialShareInfo({
+          socialShareButtonAvailable: false,
+          socialShareProviders: []
+        }));
+    });
+
+    it("should call respective mozLoop methods", function() {
+      store._handleSocialShareUpdate();
+
+      sinon.assert.calledOnce(fakeMozLoop.isSocialShareButtonAvailable);
+      sinon.assert.calledOnce(fakeMozLoop.getSocialShareProviders);
+    });
+  });
+
   describe("Events", function() {
     describe("update:{roomToken}", function() {
       beforeEach(function() {
         store.setupRoomInfo(new sharedActions.SetupRoomInfo({
           roomName: "Its a room",
           roomOwner: "Me",
           roomToken: "fakeToken",
-          roomUrl: "http://invalid"
+          roomUrl: "http://invalid",
+          socialShareButtonAvailable: false,
+          socialShareProviders: []
         }));
       });
 
       it("should dispatch an UpdateRoomInfo action", function() {
         sinon.assert.calledTwice(fakeMozLoop.rooms.on);
 
         var fakeRoomData = {
           roomName: "fakeName",
@@ -1126,17 +1181,22 @@ describe("loop.store.ActiveRoomStore", f
       var fakeRoomData = {
         roomName: "Its a room",
         roomOwner: "Me",
         roomToken: "fakeToken",
         roomUrl: "http://invalid"
       };
 
       beforeEach(function() {
-        store.setupRoomInfo(new sharedActions.SetupRoomInfo(fakeRoomData));
+        store.setupRoomInfo(new sharedActions.SetupRoomInfo(
+          _.extend(fakeRoomData, {
+            socialShareButtonAvailable: false,
+            socialShareProviders: []
+          })
+        ));
       });
 
       it("should disconnect all room connections", function() {
         fakeMozLoop.rooms.on.callArgWith(1, "delete:" + fakeRoomData.roomToken, fakeRoomData);
 
         sinon.assert.calledOnce(fakeSdkDriver.forceDisconnectAll);
       });
 
--- a/browser/components/loop/test/shared/roomStore_test.js
+++ b/browser/components/loop/test/shared/roomStore_test.js
@@ -72,16 +72,19 @@ describe("loop.store.RoomStore", functio
       pendingInitialRetrieval: false,
       rooms: [],
       activeRoom: {}
     };
 
     beforeEach(function() {
       fakeMozLoop = {
         copyString: function() {},
+        getLoopPref: function(pref) {
+          return pref;
+        },
         notifyUITour: function() {},
         rooms: {
           create: function() {},
           getAll: function() {},
           open: function() {},
           rename: function() {},
           on: sandbox.stub()
         }
@@ -363,16 +366,72 @@ describe("loop.store.RoomStore", functio
         }));
 
         sinon.assert.calledOnce(sharedUtils.composeCallUrlEmail);
         sinon.assert.calledWithExactly(sharedUtils.composeCallUrlEmail,
           "http://invalid");
       });
     });
 
+    describe("#shareRoomUrl", function() {
+      beforeEach(function() {
+        fakeMozLoop.socialShareRoom = sinon.stub();
+      });
+
+      it("should pass the correct data for GMail sharing", function() {
+        var roomUrl = "http://invalid";
+        var origin = "https://mail.google.com/v1";
+        store.shareRoomUrl(new sharedActions.ShareRoomUrl({
+          roomUrl: roomUrl,
+          provider: {
+            origin: origin
+          }
+        }));
+
+        sinon.assert.calledOnce(fakeMozLoop.socialShareRoom);
+        sinon.assert.calledWithExactly(fakeMozLoop.socialShareRoom, origin,
+          roomUrl, "", "");
+      });
+
+      it("should pass the correct data for all other Social Providers", function() {
+        var roomUrl = "http://invalid2";
+        var origin = "https://twitter.com/share";
+        store.shareRoomUrl(new sharedActions.ShareRoomUrl({
+          roomUrl: roomUrl,
+          provider: {
+            origin: origin
+          }
+        }));
+
+        sinon.assert.calledOnce(fakeMozLoop.socialShareRoom);
+        sinon.assert.calledWithExactly(fakeMozLoop.socialShareRoom, origin,
+          roomUrl, "", null);
+      });
+    });
+
+    describe("#addSocialShareButton", function() {
+      it("should invoke to the correct mozLoop function", function() {
+        fakeMozLoop.addSocialShareButton = sinon.stub();
+
+        store.addSocialShareButton(new sharedActions.AddSocialShareButton());
+
+        sinon.assert.calledOnce(fakeMozLoop.addSocialShareButton);
+      });
+    });
+
+    describe("#addSocialShareProvider", function() {
+      it("should invoke to the correct mozLoop function", function() {
+        fakeMozLoop.addSocialShareProvider = sinon.stub();
+
+        store.addSocialShareProvider(new sharedActions.AddSocialShareProvider());
+
+        sinon.assert.calledOnce(fakeMozLoop.addSocialShareProvider);
+      });
+    });
+
     describe("#setStoreState", function() {
       it("should update store state data", function() {
         store.setStoreState({pendingCreation: true});
 
         expect(store.getStoreState().pendingCreation).eql(true);
       });
 
       it("should trigger a `change` event", function(done) {