Bug 1178393 - Countdown to zero warnings in standalone test suite. r=andreio
authorChris Rafuse <chris.rafuse@priologic.com>
Tue, 11 Aug 2015 20:35:25 +0100
changeset 257240 1175ddebd4f35ad3afaef0f4d8d49a67fff5f717
parent 257239 dfbcaf688a84285e312d267757b8b7154ebb04bf
child 257241 d85445174174fb2f74b30296643b41c1c99a0499
push id14596
push usermbanner@mozilla.com
push dateTue, 11 Aug 2015 19:36:00 +0000
treeherderfx-team@1175ddebd4f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersandreio
bugs1178393
milestone43.0a1
Bug 1178393 - Countdown to zero warnings in standalone test suite. r=andreio
browser/components/loop/standalone/content/js/standaloneMetricsStore.js
browser/components/loop/standalone/content/js/standaloneRoomViews.js
browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
browser/components/loop/standalone/content/js/webapp.js
browser/components/loop/standalone/content/js/webapp.jsx
browser/components/loop/test/index.html
browser/components/loop/test/standalone/index.html
browser/components/loop/test/standalone/standaloneMetricsStore_test.js
browser/components/loop/test/standalone/webapp_test.js
browser/components/loop/ui/ui-showcase.js
browser/components/loop/ui/ui-showcase.jsx
--- a/browser/components/loop/standalone/content/js/standaloneMetricsStore.js
+++ b/browser/components/loop/standalone/content/js/standaloneMetricsStore.js
@@ -45,17 +45,18 @@ loop.store.StandaloneMetricsStore = (fun
       "connectionFailure",
       "gotMediaPermission",
       "joinRoom",
       "joinedRoom",
       "leaveRoom",
       "mediaConnected",
       "recordClick",
       "remotePeerConnected",
-      "retryAfterRoomFailure"
+      "retryAfterRoomFailure",
+      "windowUnload"
     ],
 
     /**
      * Initializes the store and starts listening to the activeRoomStore.
      *
      * @param  {Object} options Options for the store, should include a
      *                          reference to the activeRoomStore.
      */
@@ -252,13 +253,24 @@ loop.store.StandaloneMetricsStore = (fun
         return;
       }
       this._storeState[muteItem] = muted;
 
       var muteType = type === "audio" ? METRICS_GA_ACTIONS.audioMute : METRICS_GA_ACTIONS.faceMute;
       var muteState = muted ? "mute" : "unmute";
 
       this._storeEvent(METRICS_GA_CATEGORY.general, muteType, muteState);
+    },
+
+    /**
+     * Called when the window is unloaded, either by code, or by the user
+     * explicitly closing it.  Expected to do any necessary housekeeping, such
+     * as shutting down the call cleanly and adding any relevant telemetry data.
+     */
+    windowUnload: function() {
+      if (this.activeRoomStore) {
+        this.stopListening(this.activeRoomStore);
+      }
     }
   });
 
   return StandaloneMetricsStore;
 })();
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -409,16 +409,18 @@ loop.standaloneRoomViews = (function(moz
         case ROOM_STATES.JOINED:
         case ROOM_STATES.MEDIA_WAIT:
           // this case is so that we don't show an avatar while waiting for
           // the other party to connect
           return true;
 
         case ROOM_STATES.FAILED:
         case ROOM_STATES.CLOSING:
+        case ROOM_STATES.FULL:
+        case ROOM_STATES.ENDED:
           // the other person has shown up, so we don't want to show an avatar
           return true;
 
         default:
           console.warn("StandaloneRoomView.shouldRenderRemoteVideo:" +
             " unexpected roomState: ", this.state.roomState);
           return true;
 
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -409,16 +409,18 @@ loop.standaloneRoomViews = (function(moz
         case ROOM_STATES.JOINED:
         case ROOM_STATES.MEDIA_WAIT:
           // this case is so that we don't show an avatar while waiting for
           // the other party to connect
           return true;
 
         case ROOM_STATES.FAILED:
         case ROOM_STATES.CLOSING:
+        case ROOM_STATES.FULL:
+        case ROOM_STATES.ENDED:
           // the other person has shown up, so we don't want to show an avatar
           return true;
 
         default:
           console.warn("StandaloneRoomView.shouldRenderRemoteVideo:" +
             " unexpected roomState: ", this.state.roomState);
           return true;
 
--- a/browser/components/loop/standalone/content/js/webapp.js
+++ b/browser/components/loop/standalone/content/js/webapp.js
@@ -88,17 +88,17 @@ loop.webapp = (function($, _, OT, mozL10
    */
   var PromoteFirefoxView = React.createClass({displayName: "PromoteFirefoxView",
     propTypes: {
       isFirefox: React.PropTypes.bool.isRequired
     },
 
     render: function() {
       if (this.props.isFirefox) {
-        return React.createElement("div", null);
+        return null;
       }
       return (
         React.createElement("div", {className: "promote-firefox"}, 
           React.createElement("h3", null, mozL10n.get("promote_firefox_hello_heading", {brandShortname: mozL10n.get("brandShortname")})), 
           React.createElement("p", null, 
             React.createElement("a", {className: "btn btn-large btn-accept", 
                href: loop.config.downloadFirefoxUrl}, 
               mozL10n.get("get_firefox_button", {
@@ -644,18 +644,18 @@ loop.webapp = (function($, _, OT, mozL10
   var OutgoingConversationView = React.createClass({displayName: "OutgoingConversationView",
     propTypes: {
       client: React.PropTypes.instanceOf(loop.StandaloneClient).isRequired,
       conversation: React.PropTypes.oneOfType([
         React.PropTypes.instanceOf(sharedModels.ConversationModel),
         React.PropTypes.instanceOf(FxOSConversationModel)
       ]).isRequired,
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
-                          .isRequired,
+      isFirefox: React.PropTypes.bool.isRequired,
+      notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection).isRequired,
       sdk: React.PropTypes.object.isRequired
     },
 
     getInitialState: function() {
       return {
         callStatus: "start"
       };
     },
@@ -733,17 +733,17 @@ loop.webapp = (function($, _, OT, mozL10
               conversation: this.props.conversation, 
               dispatcher: this.props.dispatcher, 
               onAfterFeedbackReceived: this.resetCallStatus(), 
               sdk: this.props.sdk})
           );
         }
         case "expired": {
           return (
-            React.createElement(CallUrlExpiredView, null)
+            React.createElement(CallUrlExpiredView, {isFirefox: this.props.isFirefox})
           );
         }
         default: {
           return React.createElement(HomeView, null);
         }
       }
     },
 
@@ -981,16 +981,17 @@ loop.webapp = (function($, _, OT, mozL10
           return React.createElement(UnsupportedBrowserView, {isFirefox: this.state.isFirefox});
         }
         case "outgoing": {
           return (
             React.createElement(OutgoingConversationView, {
                client: this.props.client, 
                conversation: this.props.conversation, 
                dispatcher: this.props.dispatcher, 
+               isFirefox: this.state.isFirefox, 
                notifications: this.props.notifications, 
                sdk: this.props.sdk})
           );
         }
         case "room": {
           return (
             React.createElement(loop.standaloneRoomViews.StandaloneRoomView, {
               activeRoomStore: this.props.activeRoomStore, 
--- a/browser/components/loop/standalone/content/js/webapp.jsx
+++ b/browser/components/loop/standalone/content/js/webapp.jsx
@@ -88,17 +88,17 @@ loop.webapp = (function($, _, OT, mozL10
    */
   var PromoteFirefoxView = React.createClass({
     propTypes: {
       isFirefox: React.PropTypes.bool.isRequired
     },
 
     render: function() {
       if (this.props.isFirefox) {
-        return <div />;
+        return null;
       }
       return (
         <div className="promote-firefox">
           <h3>{mozL10n.get("promote_firefox_hello_heading", {brandShortname: mozL10n.get("brandShortname")})}</h3>
           <p>
             <a className="btn btn-large btn-accept"
                href={loop.config.downloadFirefoxUrl}>
               {mozL10n.get("get_firefox_button", {
@@ -644,18 +644,18 @@ loop.webapp = (function($, _, OT, mozL10
   var OutgoingConversationView = React.createClass({
     propTypes: {
       client: React.PropTypes.instanceOf(loop.StandaloneClient).isRequired,
       conversation: React.PropTypes.oneOfType([
         React.PropTypes.instanceOf(sharedModels.ConversationModel),
         React.PropTypes.instanceOf(FxOSConversationModel)
       ]).isRequired,
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
-                          .isRequired,
+      isFirefox: React.PropTypes.bool.isRequired,
+      notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection).isRequired,
       sdk: React.PropTypes.object.isRequired
     },
 
     getInitialState: function() {
       return {
         callStatus: "start"
       };
     },
@@ -733,17 +733,17 @@ loop.webapp = (function($, _, OT, mozL10
               conversation={this.props.conversation}
               dispatcher={this.props.dispatcher}
               onAfterFeedbackReceived={this.resetCallStatus()}
               sdk={this.props.sdk} />
           );
         }
         case "expired": {
           return (
-            <CallUrlExpiredView />
+            <CallUrlExpiredView isFirefox={this.props.isFirefox}/>
           );
         }
         default: {
           return <HomeView />;
         }
       }
     },
 
@@ -981,16 +981,17 @@ loop.webapp = (function($, _, OT, mozL10
           return <UnsupportedBrowserView isFirefox={this.state.isFirefox}/>;
         }
         case "outgoing": {
           return (
             <OutgoingConversationView
                client={this.props.client}
                conversation={this.props.conversation}
                dispatcher={this.props.dispatcher}
+               isFirefox={this.state.isFirefox}
                notifications={this.props.notifications}
                sdk={this.props.sdk} />
           );
         }
         case "room": {
           return (
             <loop.standaloneRoomViews.StandaloneRoomView
               activeRoomStore={this.props.activeRoomStore}
--- a/browser/components/loop/test/index.html
+++ b/browser/components/loop/test/index.html
@@ -9,11 +9,12 @@
 </head>
 <body>
   <h1>Loop tests</h1>
   <ul>
     <li><a href="shared/">Shared tests</a></li>
     <li><a href="desktop-local/">Local tests</a></li>
     <li><a href="standalone/">Standalone tests</a></li>
     <li><a href="coverage/">Code Coverage</a></li>
+    <li><a href="../ui/">UI Showcase</a></li>
  </ul>
 </body>
 </html>
--- a/browser/components/loop/test/standalone/index.html
+++ b/browser/components/loop/test/standalone/index.html
@@ -79,18 +79,18 @@
   <script>
     describe("Uncaught Error Check", function() {
       it("should load the tests without errors", function() {
         chai.expect(uncaughtError && uncaughtError.message).to.be.undefined;
       });
     });
 
     describe("Unexpected Warnings Check", function() {
-      it("should long only the warnings we expect", function() {
-        chai.expect(caughtWarnings.length).to.eql(10);
+      it("should log only the warnings we expect", function() {
+        chai.expect(caughtWarnings.length).to.eql(0);
       });
     });
 
     mocha.run(function () {
       var completeNode = document.createElement("p");
       completeNode.setAttribute("id", "complete");
       completeNode.appendChild(document.createTextNode("Complete"));
       document.getElementById("mocha").appendChild(completeNode);
--- a/browser/components/loop/test/standalone/standaloneMetricsStore_test.js
+++ b/browser/components/loop/test/standalone/standaloneMetricsStore_test.js
@@ -190,10 +190,27 @@ describe("loop.store.StandaloneMetricsSt
         audioMuted: true
       });
 
       sinon.assert.calledOnce(window.ga);
       sinon.assert.calledWithExactly(window.ga,
         "send", "event", METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.audioMute,
         "mute");
     });
+
+    describe("Event listeners", function() {
+      it("should call windowUnload when action is dispatched", function() {
+        sandbox.stub(store, "windowUnload");
+
+        dispatcher.dispatch(new sharedActions.WindowUnload());
+        sinon.assert.calledOnce(store.windowUnload);
+      });
+
+      it("should stop listening to activeRoomStore", function() {
+        var stopListeningStub = sandbox.stub(store, "stopListening");
+        store.windowUnload();
+
+        sinon.assert.calledOnce(stopListeningStub);
+        sinon.assert.calledWithExactly(stopListeningStub, store.activeRoomStore);
+      });
+    });
   });
 });
--- a/browser/components/loop/test/standalone/webapp_test.js
+++ b/browser/components/loop/test/standalone/webapp_test.js
@@ -12,26 +12,31 @@ describe("loop.webapp", function() {
       sharedViews = loop.shared.views,
       sharedUtils = loop.shared.utils,
       standaloneMedia = loop.standaloneMedia,
       sandbox,
       notifications,
       stubGetPermsAndCacheMedia,
       fakeAudioXHR,
       dispatcher,
+      mozL10nGet,
       WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     dispatcher = new loop.Dispatcher();
     notifications = new sharedModels.NotificationCollection();
 
     stubGetPermsAndCacheMedia = sandbox.stub(
       loop.standaloneMedia._MultiplexGum.prototype, "getPermsAndCacheMedia");
 
+    mozL10nGet = sandbox.stub(navigator.mozL10n, "get", function(x) {
+      return "translated:" + x;
+    });
+
     fakeAudioXHR = {
       open: sinon.spy(),
       send: function() {},
       abort: function() {},
       getResponseHeader: function(header) {
         if (header === "Content-Type") {
           return "audio/ogg";
         }
@@ -109,16 +114,17 @@ describe("loop.webapp", function() {
       sandbox.stub(client, "requestCallUrlInfo");
       conversation = new sharedModels.ConversationModel({}, {
         sdk: {}
       });
       conversation.set("loopToken", "fakeToken");
       ocView = mountTestComponent({
         client: client,
         conversation: conversation,
+        isFirefox: true,
         notifications: notifications,
         sdk: {
           on: sandbox.stub()
         },
         dispatcher: dispatcher
       });
     });
 
@@ -672,56 +678,55 @@ describe("loop.webapp", function() {
       });
       // Stub this to stop the StartConversationView kicking in the request and
       // follow-ups.
       sandbox.stub(client, "requestCallUrlInfo");
     });
 
     it("should display the UnsupportedDeviceView for `unsupportedDevice` window type",
       function() {
-        standaloneAppStore.setStoreState({windowType: "unsupportedDevice"});
-
+        standaloneAppStore.setStoreState({windowType: "unsupportedDevice", unsupportedPlatform: "ios"});
         var webappRootView = mountTestComponent();
 
         TestUtils.findRenderedComponentWithType(webappRootView,
           loop.webapp.UnsupportedDeviceView);
       });
 
     it("should display the UnsupportedBrowserView for `unsupportedBrowser` window type",
       function() {
-        standaloneAppStore.setStoreState({windowType: "unsupportedBrowser"});
+        standaloneAppStore.setStoreState({windowType: "unsupportedBrowser", isFirefox: false});
 
         var webappRootView = mountTestComponent();
 
         TestUtils.findRenderedComponentWithType(webappRootView,
           loop.webapp.UnsupportedBrowserView);
       });
 
     it("should display the OutgoingConversationView for `outgoing` window type",
       function() {
-        standaloneAppStore.setStoreState({windowType: "outgoing"});
+        standaloneAppStore.setStoreState({windowType: "outgoing", isFirefox: true});
 
         var webappRootView = mountTestComponent();
 
         TestUtils.findRenderedComponentWithType(webappRootView,
           loop.webapp.OutgoingConversationView);
       });
 
     it("should display the StandaloneRoomView for `room` window type",
       function() {
-        standaloneAppStore.setStoreState({windowType: "room"});
+        standaloneAppStore.setStoreState({windowType: "room", isFirefox: true});
 
         var webappRootView = mountTestComponent();
 
         TestUtils.findRenderedComponentWithType(webappRootView,
           loop.standaloneRoomViews.StandaloneRoomView);
       });
 
     it("should display the HomeView for `home` window type", function() {
-        standaloneAppStore.setStoreState({windowType: "home"});
+        standaloneAppStore.setStoreState({windowType: "home", isFirefox: true});
 
         var webappRootView = mountTestComponent();
 
         TestUtils.findRenderedComponentWithType(webappRootView,
           loop.webapp.HomeView);
     });
   });
 
@@ -1085,29 +1090,29 @@ describe("loop.webapp", function() {
   });
 
   describe("PromoteFirefoxView", function() {
     describe("#render", function() {
       it("should not render when using Firefox", function() {
         var comp = TestUtils.renderIntoDocument(
           React.createElement(loop.webapp.PromoteFirefoxView, {
             isFirefox: true
-          }));
+        }));
 
-        expect(comp.getDOMNode().querySelectorAll("h3").length).eql(0);
+        expect(comp.getDOMNode()).eql(null);
       });
 
       it("should render when not using Firefox", function() {
         var comp = TestUtils.renderIntoDocument(
-          React.createElement(
-            loop.webapp.PromoteFirefoxView, {
+          React.createElement(loop.webapp.PromoteFirefoxView, {
               isFirefox: false
-            }));
+        }));
 
-        expect(comp.getDOMNode().querySelectorAll("h3").length).eql(1);
+        sinon.assert.calledWith(mozL10nGet, "promote_firefox_hello_heading");
+        sinon.assert.calledWith(mozL10nGet, "get_firefox_button");
       });
     });
   });
 
   describe("Firefox OS", function() {
     var conversation, client;
 
     before(function() {
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -1536,17 +1536,17 @@
         setTimeout(waitForQueuedFrames, 500);
         return;
       }
       // Put the title back, in case views changed it.
       document.title = "Loop UI Components Showcase";
 
       // This simulates the mocha layout for errors which means we can run
       // this alongside our other unit tests but use the same harness.
-      var expectedWarningsCount = 18;
+      var expectedWarningsCount = 16;
       var warningsMismatch = caughtWarnings.length !== expectedWarningsCount;
       if (uncaughtError || warningsMismatch) {
         $("#results").append("<div class='failures'><em>" +
           ((uncaughtError && warningsMismatch) ? 2 : 1) + "</em></div>");
         if (warningsMismatch) {
           $("#results").append("<li class='test fail'>" +
             "<h2>Unexpected number of warnings detected in UI-Showcase</h2>" +
             "<pre class='error'>Got: " + caughtWarnings.length + "\n" +
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -1536,17 +1536,17 @@
         setTimeout(waitForQueuedFrames, 500);
         return;
       }
       // Put the title back, in case views changed it.
       document.title = "Loop UI Components Showcase";
 
       // This simulates the mocha layout for errors which means we can run
       // this alongside our other unit tests but use the same harness.
-      var expectedWarningsCount = 18;
+      var expectedWarningsCount = 16;
       var warningsMismatch = caughtWarnings.length !== expectedWarningsCount;
       if (uncaughtError || warningsMismatch) {
         $("#results").append("<div class='failures'><em>" +
           ((uncaughtError && warningsMismatch) ? 2 : 1) + "</em></div>");
         if (warningsMismatch) {
           $("#results").append("<li class='test fail'>" +
             "<h2>Unexpected number of warnings detected in UI-Showcase</h2>" +
             "<pre class='error'>Got: " + caughtWarnings.length + "\n" +