Bug 1171940: update/ fix tests that my patches above have broken and lower the total amount of warnings generated by React in the console. r=Standard8, a=ritu
authorMike de Boer <mdeboer@mozilla.com>
Thu, 16 Jul 2015 16:39:18 +0200
changeset 281543 0c8e3181f2f6fc51af1404122b73f3398f1a43d0
parent 281542 bed330496c8b0f71ddf0ec021e0997098e1674f2
child 281544 b411cef27c22022133234a38aee6261c57ca43e8
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersStandard8, ritu
bugs1171940
milestone41.0a2
Bug 1171940: update/ fix tests that my patches above have broken and lower the total amount of warnings generated by React in the console. r=Standard8, a=ritu
browser/components/loop/content/js/contacts.js
browser/components/loop/content/js/contacts.jsx
browser/components/loop/content/js/conversationViews.js
browser/components/loop/content/js/conversationViews.jsx
browser/components/loop/content/js/roomViews.js
browser/components/loop/content/js/roomViews.jsx
browser/components/loop/content/shared/js/views.js
browser/components/loop/content/shared/js/views.jsx
browser/components/loop/standalone/content/js/standaloneRoomViews.js
browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
browser/components/loop/test/desktop-local/contacts_test.js
browser/components/loop/test/desktop-local/conversation_test.js
browser/components/loop/test/desktop-local/panel_test.js
browser/components/loop/test/desktop-local/roomViews_test.js
browser/components/loop/test/shared/textChatView_test.js
browser/components/loop/test/shared/views_test.js
--- a/browser/components/loop/content/js/contacts.js
+++ b/browser/components/loop/content/js/contacts.js
@@ -124,17 +124,19 @@ loop.contacts = (function(_, mozL10n) {
         "learn_more": React.renderToStaticMarkup(
           React.createElement("a", {href: privacyUrl, target: "_blank"}, 
             mozL10n.get("gravatars_promo_message_learnmore")
           )
         )
       });
       return (
         React.createElement("div", {className: "contacts-gravatar-promo"}, 
-          React.createElement(Button, {additionalClass: "button-close", onClick: this.handleCloseButtonClick}), 
+          React.createElement(Button, {additionalClass: "button-close", 
+                  caption: "", 
+                  onClick: this.handleCloseButtonClick}), 
           React.createElement("p", {dangerouslySetInnerHTML: {__html: message}, 
              onClick: this.handleLinkClick}), 
           React.createElement(ButtonGroup, null, 
             React.createElement(Button, {caption: mozL10n.get("gravatars_promo_button_nothanks"), 
                     onClick: this.handleCloseButtonClick}), 
             React.createElement(Button, {additionalClass: "button-accept", 
                     caption: mozL10n.get("gravatars_promo_button_use"), 
                     onClick: this.handleUseButtonClick})
--- a/browser/components/loop/content/js/contacts.jsx
+++ b/browser/components/loop/content/js/contacts.jsx
@@ -124,17 +124,19 @@ loop.contacts = (function(_, mozL10n) {
         "learn_more": React.renderToStaticMarkup(
           <a href={privacyUrl} target="_blank">
             {mozL10n.get("gravatars_promo_message_learnmore")}
           </a>
         )
       });
       return (
         <div className="contacts-gravatar-promo">
-          <Button additionalClass="button-close" onClick={this.handleCloseButtonClick}/>
+          <Button additionalClass="button-close"
+                  caption=""
+                  onClick={this.handleCloseButtonClick} />
           <p dangerouslySetInnerHTML={{__html: message}}
              onClick={this.handleLinkClick}></p>
           <ButtonGroup>
             <Button caption={mozL10n.get("gravatars_promo_button_nothanks")}
                     onClick={this.handleCloseButtonClick}/>
             <Button additionalClass="button-accept"
                     caption={mozL10n.get("gravatars_promo_button_use")}
                     onClick={this.handleUseButtonClick}/>
--- a/browser/components/loop/content/js/conversationViews.js
+++ b/browser/components/loop/content/js/conversationViews.js
@@ -654,23 +654,25 @@ loop.conversationViews = (function(mozL1
 
       return (
         React.createElement("div", {className: "video-layout-wrapper"}, 
           React.createElement("div", {className: "conversation"}, 
             React.createElement("div", {className: "media nested"}, 
               React.createElement("div", {className: "video_wrapper remote_wrapper"}, 
                 React.createElement("div", {className: "video_inner remote focus-stream"}, 
                   React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(), 
+                    isLoading: false, 
                     mediaType: "remote", 
                     posterUrl: this.props.remotePosterUrl, 
                     srcVideoObject: this.state.remoteSrcVideoObject})
                 )
               ), 
               React.createElement("div", {className: localStreamClasses}, 
                 React.createElement(sharedViews.MediaView, {displayAvatar: !this.props.video.enabled, 
+                  isLoading: false, 
                   mediaType: "local", 
                   posterUrl: this.props.localPosterUrl, 
                   srcVideoObject: this.state.localSrcVideoObject})
               )
             ), 
             React.createElement(loop.shared.views.ConversationToolbar, {
               audio: this.props.audio, 
               dispatcher: this.props.dispatcher, 
--- a/browser/components/loop/content/js/conversationViews.jsx
+++ b/browser/components/loop/content/js/conversationViews.jsx
@@ -654,23 +654,25 @@ loop.conversationViews = (function(mozL1
 
       return (
         <div className="video-layout-wrapper">
           <div className="conversation">
             <div className="media nested">
               <div className="video_wrapper remote_wrapper">
                 <div className="video_inner remote focus-stream">
                   <sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
+                    isLoading={false}
                     mediaType="remote"
                     posterUrl={this.props.remotePosterUrl}
                     srcVideoObject={this.state.remoteSrcVideoObject} />
                 </div>
               </div>
               <div className={localStreamClasses}>
                 <sharedViews.MediaView displayAvatar={!this.props.video.enabled}
+                  isLoading={false}
                   mediaType="local"
                   posterUrl={this.props.localPosterUrl}
                   srcVideoObject={this.state.localSrcVideoObject} />
               </div>
             </div>
             <loop.shared.views.ConversationToolbar
               audio={this.props.audio}
               dispatcher={this.props.dispatcher}
--- a/browser/components/loop/content/js/roomViews.js
+++ b/browser/components/loop/content/js/roomViews.js
@@ -283,18 +283,16 @@ loop.roomViews = (function(mozL10n) {
       );
     }
   });
 
   var DesktopRoomEditContextView = React.createClass({displayName: "DesktopRoomEditContextView",
     mixins: [React.addons.LinkedStateMixin],
 
     propTypes: {
-      // Only used for tests.
-      availableContext: React.PropTypes.object,
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       error: React.PropTypes.object,
       mozLoop: React.PropTypes.object.isRequired,
       onClose: React.PropTypes.func,
       // This data is supplied by the activeRoomStore.
       roomData: React.PropTypes.object.isRequired,
       savingContext: React.PropTypes.bool.isRequired,
       show: React.PropTypes.bool.isRequired
@@ -352,17 +350,17 @@ loop.roomViews = (function(mozL10n) {
         this.setState(newState);
       }
     },
 
     getInitialState: function() {
       var url = this._getURL();
       return {
         // `availableContext` prop only used in tests.
-        availableContext: this.props.availableContext,
+        availableContext: null,
         show: this.props.show,
         newRoomName: this.props.roomData.roomName || "",
         newRoomURL: url && url.location || "",
         newRoomDescription: url && url.description || "",
         newRoomThumbnail: url && url.thumbnail || ""
       };
     },
 
@@ -670,19 +668,19 @@ loop.roomViews = (function(mozL10n) {
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a remote
      * stream is on its way from the other user?
      *
      * @returns {boolean}
      * @private
      */
     _shouldRenderRemoteLoading: function() {
-      return this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
-             !this.state.remoteSrcVideoObject &&
-             !this.state.mediaConnected;
+      return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
+                !this.state.remoteSrcVideoObject &&
+                !this.state.mediaConnected);
     },
 
     handleAddContextClick: function() {
       this.setState({ showEditContext: true });
     },
 
     handleEditContextClick: function() {
       this.setState({ showEditContext: !this.state.showEditContext });
@@ -700,17 +698,17 @@ loop.roomViews = (function(mozL10n) {
       var localStreamClasses = React.addons.classSet({
         local: true,
         "local-stream": true,
         "local-stream-audio": this.state.videoMuted,
         "room-preview": this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS
       });
 
       var screenShareData = {
-        state: this.state.screenSharingState,
+        state: this.state.screenSharingState || SCREEN_SHARE_STATES.INACTIVE,
         visible: true
       };
 
       var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay();
       var shouldRenderEditContextView = this.state.contextEnabled && this.state.showEditContext;
       var roomData = this.props.roomStore.getStoreState("activeRoom");
 
       switch(this.state.roomState) {
--- a/browser/components/loop/content/js/roomViews.jsx
+++ b/browser/components/loop/content/js/roomViews.jsx
@@ -283,18 +283,16 @@ loop.roomViews = (function(mozL10n) {
       );
     }
   });
 
   var DesktopRoomEditContextView = React.createClass({
     mixins: [React.addons.LinkedStateMixin],
 
     propTypes: {
-      // Only used for tests.
-      availableContext: React.PropTypes.object,
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       error: React.PropTypes.object,
       mozLoop: React.PropTypes.object.isRequired,
       onClose: React.PropTypes.func,
       // This data is supplied by the activeRoomStore.
       roomData: React.PropTypes.object.isRequired,
       savingContext: React.PropTypes.bool.isRequired,
       show: React.PropTypes.bool.isRequired
@@ -352,17 +350,17 @@ loop.roomViews = (function(mozL10n) {
         this.setState(newState);
       }
     },
 
     getInitialState: function() {
       var url = this._getURL();
       return {
         // `availableContext` prop only used in tests.
-        availableContext: this.props.availableContext,
+        availableContext: null,
         show: this.props.show,
         newRoomName: this.props.roomData.roomName || "",
         newRoomURL: url && url.location || "",
         newRoomDescription: url && url.description || "",
         newRoomThumbnail: url && url.thumbnail || ""
       };
     },
 
@@ -670,19 +668,19 @@ loop.roomViews = (function(mozL10n) {
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a remote
      * stream is on its way from the other user?
      *
      * @returns {boolean}
      * @private
      */
     _shouldRenderRemoteLoading: function() {
-      return this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
-             !this.state.remoteSrcVideoObject &&
-             !this.state.mediaConnected;
+      return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
+                !this.state.remoteSrcVideoObject &&
+                !this.state.mediaConnected);
     },
 
     handleAddContextClick: function() {
       this.setState({ showEditContext: true });
     },
 
     handleEditContextClick: function() {
       this.setState({ showEditContext: !this.state.showEditContext });
@@ -700,17 +698,17 @@ loop.roomViews = (function(mozL10n) {
       var localStreamClasses = React.addons.classSet({
         local: true,
         "local-stream": true,
         "local-stream-audio": this.state.videoMuted,
         "room-preview": this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS
       });
 
       var screenShareData = {
-        state: this.state.screenSharingState,
+        state: this.state.screenSharingState || SCREEN_SHARE_STATES.INACTIVE,
         visible: true
       };
 
       var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay();
       var shouldRenderEditContextView = this.state.contextEnabled && this.state.showEditContext;
       var roomData = this.props.roomStore.getStoreState("activeRoom");
 
       switch(this.state.roomState) {
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/components/loop/content/shared/js/views.js
@@ -318,17 +318,17 @@ loop.shared.views = (function(_, l10n) {
           // If there's no getSources function, the sdk defines its own and caches
           // the result. So here we define the "normal" one which doesn't get cached, so
           // we can change it later.
           window.MediaStreamTrack.getSources = function(callback) {
             callback([{kind: "audio"}, {kind: "video"}]);
           };
         }
 
-        this.listenTo(this.props.sdk, "exception", this._handleSdkException.bind(this));
+        this.listenTo(this.props.sdk, "exception", this._handleSdkException);
 
         this.listenTo(this.props.model, "session:connected",
                                         this._onSessionConnected);
         this.listenTo(this.props.model, "session:stream-created",
                                         this._streamCreated);
         this.listenTo(this.props.model, ["session:peer-hungup",
                                          "session:network-disconnected",
                                          "session:ended"].join(" "),
@@ -420,24 +420,24 @@ loop.shared.views = (function(_, l10n) {
                       ev.preventDefault();
                     });
 
       this.listenTo(this.publisher, "streamCreated", function(ev) {
         this.setState({
           audio: {enabled: ev.stream.hasAudio},
           video: {enabled: ev.stream.hasVideo}
         });
-      }.bind(this));
+      });
 
       this.listenTo(this.publisher, "streamDestroyed", function() {
         this.setState({
           audio: {enabled: false},
           video: {enabled: false}
         });
-      }.bind(this));
+      });
 
       this.props.model.publish(this.publisher);
     },
 
     /**
      * Toggles streaming status for a given stream type.
      *
      * @param  {String}  type     Stream type ("audio" or "video").
@@ -537,17 +537,17 @@ loop.shared.views = (function(_, l10n) {
 
     getDefaultProps: function() {
       return {clearOnDocumentHidden: false};
     },
 
     componentDidMount: function() {
       this.listenTo(this.props.notifications, "reset add remove", function() {
         this.forceUpdate();
-      }.bind(this));
+      });
     },
 
     componentWillUnmount: function() {
       this.stopListening(this.props.notifications);
     },
 
     /**
      * Provided by DocumentVisibilityMixin. Clears notifications stack when the
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/components/loop/content/shared/js/views.jsx
@@ -318,17 +318,17 @@ loop.shared.views = (function(_, l10n) {
           // If there's no getSources function, the sdk defines its own and caches
           // the result. So here we define the "normal" one which doesn't get cached, so
           // we can change it later.
           window.MediaStreamTrack.getSources = function(callback) {
             callback([{kind: "audio"}, {kind: "video"}]);
           };
         }
 
-        this.listenTo(this.props.sdk, "exception", this._handleSdkException.bind(this));
+        this.listenTo(this.props.sdk, "exception", this._handleSdkException);
 
         this.listenTo(this.props.model, "session:connected",
                                         this._onSessionConnected);
         this.listenTo(this.props.model, "session:stream-created",
                                         this._streamCreated);
         this.listenTo(this.props.model, ["session:peer-hungup",
                                          "session:network-disconnected",
                                          "session:ended"].join(" "),
@@ -420,24 +420,24 @@ loop.shared.views = (function(_, l10n) {
                       ev.preventDefault();
                     });
 
       this.listenTo(this.publisher, "streamCreated", function(ev) {
         this.setState({
           audio: {enabled: ev.stream.hasAudio},
           video: {enabled: ev.stream.hasVideo}
         });
-      }.bind(this));
+      });
 
       this.listenTo(this.publisher, "streamDestroyed", function() {
         this.setState({
           audio: {enabled: false},
           video: {enabled: false}
         });
-      }.bind(this));
+      });
 
       this.props.model.publish(this.publisher);
     },
 
     /**
      * Toggles streaming status for a given stream type.
      *
      * @param  {String}  type     Stream type ("audio" or "video").
@@ -537,17 +537,17 @@ loop.shared.views = (function(_, l10n) {
 
     getDefaultProps: function() {
       return {clearOnDocumentHidden: false};
     },
 
     componentDidMount: function() {
       this.listenTo(this.props.notifications, "reset add remove", function() {
         this.forceUpdate();
-      }.bind(this));
+      });
     },
 
     componentWillUnmount: function() {
       this.stopListening(this.props.notifications);
     },
 
     /**
      * Provided by DocumentVisibilityMixin. Clears notifications stack when the
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -389,19 +389,19 @@ loop.standaloneRoomViews = (function(moz
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a remote
      * stream is on its way from the other user?
      *
      * @returns {boolean}
      * @private
      */
     _shouldRenderRemoteLoading: function() {
-      return this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
-             !this.state.remoteSrcVideoObject &&
-             !this.state.mediaConnected;
+      return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
+                !this.state.remoteSrcVideoObject &&
+                !this.state.mediaConnected);
     },
 
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a remote
      * screen-share is on its way from the other user?
      *
      * @returns {boolean}
      * @private
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -389,19 +389,19 @@ loop.standaloneRoomViews = (function(moz
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a remote
      * stream is on its way from the other user?
      *
      * @returns {boolean}
      * @private
      */
     _shouldRenderRemoteLoading: function() {
-      return this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
-             !this.state.remoteSrcVideoObject &&
-             !this.state.mediaConnected;
+      return !!(this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS &&
+                !this.state.remoteSrcVideoObject &&
+                !this.state.mediaConnected);
     },
 
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a remote
      * screen-share is on its way from the other user?
      *
      * @returns {boolean}
      * @private
--- a/browser/components/loop/test/desktop-local/contacts_test.js
+++ b/browser/components/loop/test/desktop-local/contacts_test.js
@@ -141,17 +141,18 @@ describe("loop.contacts", function() {
       // Sanity check the reverse:
       gravatars = node.querySelectorAll(".contact img[src=gravatarsDisabled]");
       expect(gravatars.length).to.equal(enabled ? 0 : fakeContacts.length);
     }
 
     it("should show the gravatars promo box", function() {
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
-          notifications: notifications
+          notifications: notifications,
+          startForm: function() {}
         }));
 
       var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
       expect(promo).to.not.equal(null);
 
       checkGravatarContacts(false);
     });
 
@@ -161,71 +162,76 @@ describe("loop.contacts", function() {
           return false;
         } else if (pref == "contacts.gravatars.show") {
           return true;
         }
         return "";
       };
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
-          notifications: notifications
+          notifications: notifications,
+          startForm: function() {}
         }));
 
       var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
       expect(promo).to.equal(null);
 
       checkGravatarContacts(true);
     });
 
     it("should hide the gravatars promo box when the 'use' button is clicked", function() {
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
-          notifications: notifications
+          notifications: notifications,
+          startForm: function() {}
         }));
 
       React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
         ".contacts-gravatar-promo .button-accept"));
 
       sinon.assert.calledTwice(navigator.mozLoop.setLoopPref);
 
       var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
       expect(promo).to.equal(null);
     });
 
     it("should should set the prefs correctly when the 'use' button is clicked", function() {
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
-          notifications: notifications
+          notifications: notifications,
+          startForm: function() {}
         }));
 
       React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
         ".contacts-gravatar-promo .button-accept"));
 
       sinon.assert.calledTwice(navigator.mozLoop.setLoopPref);
       sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref, "contacts.gravatars.promo", false);
       sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref, "contacts.gravatars.show", true);
     });
 
     it("should hide the gravatars promo box when the 'close' button is clicked", function() {
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
-          notifications: notifications
+          notifications: notifications,
+          startForm: function() {}
         }));
 
       React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
         ".contacts-gravatar-promo .button-close"));
 
       var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
       expect(promo).to.equal(null);
     });
 
     it("should set prefs correctly when the 'close' button is clicked", function() {
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
-          notifications: notifications
+          notifications: notifications,
+          startForm: function() {}
         }));
 
       React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
         ".contacts-gravatar-promo .button-close"));
 
       sinon.assert.calledOnce(navigator.mozLoop.setLoopPref);
       sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref,
         "contacts.gravatars.promo", false);
@@ -237,17 +243,18 @@ describe("loop.contacts", function() {
       navigator.mozLoop.calls = {
         startDirectCall: sandbox.stub(),
         clearCallInProgress: sandbox.stub()
       };
       navigator.mozLoop.contacts = {getAll: sandbox.stub()};
 
       listView = TestUtils.renderIntoDocument(
         React.createElement(loop.contacts.ContactsList, {
-          notifications: notifications
+          notifications: notifications,
+          startForm: function() {}
         }));
     });
 
     describe("#handleContactAction", function() {
       it("should call window.close when called with 'video-call' action",
         function() {
           listView.handleContactAction({}, "video-call");
 
@@ -294,18 +301,20 @@ describe("loop.contacts", function() {
 
   describe("ContactDetailsForm", function() {
     describe("#render", function() {
       describe("add mode", function() {
         var view;
 
         beforeEach(function() {
           view = TestUtils.renderIntoDocument(
-            React.createElement(
-              loop.contacts.ContactDetailsForm, {mode: "add"}));
+            React.createElement(loop.contacts.ContactDetailsForm, {
+              mode: "add",
+              selectTab: function() {}
+            }));
         });
 
         it("should render 'add' header", function() {
 
           var header = view.getDOMNode().querySelector("header");
           expect(header).to.not.equal(null);
           expect(header.textContent).to.eql(fakeAddContactButtonText);
         });
@@ -404,18 +413,20 @@ describe("loop.contacts", function() {
         });
       });
 
       describe("edit mode", function() {
         var view;
 
         beforeEach(function() {
           view = TestUtils.renderIntoDocument(
-            React.createElement(
-              loop.contacts.ContactDetailsForm, {mode: "edit"}));
+            React.createElement(loop.contacts.ContactDetailsForm, {
+              mode: "edit",
+              selectTab: function() {}
+            }));
         });
 
         it("should render 'edit' header", function() {
           var header = view.getDOMNode().querySelector("header");
           expect(header).to.not.equal(null);
           expect(header.textContent).to.eql(fakeEditContactButtonText);
         });
 
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -41,17 +41,20 @@ describe("loop.conversation", function()
         return {
           version: "42",
           channel: "test",
           platform: "test"
         };
       },
       getAudioBlob: sinon.spy(function(name, callback) {
         callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
-      })
+      }),
+      getSelectedTabMetadata: function(callback) {
+        callback({});
+      }
     };
 
     fakeWindow = {
       navigator: { mozLoop: navigator.mozLoop },
       close: sinon.stub(),
       document: {},
       addEventListener: function() {},
       removeEventListener: function() {}
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -775,17 +775,17 @@ describe("loop.panel", function() {
           userDisplayName: fakeEmail
         }));
     }
 
     it("should dispatch a CreateRoom action when clicking on the Start a " +
        "conversation button",
       function() {
         navigator.mozLoop.userProfile = {email: fakeEmail};
-        var view = createTestComponent();
+        var view = createTestComponent(false);
 
         TestUtils.Simulate.click(view.getDOMNode().querySelector(".new-room-button"));
 
         sinon.assert.calledWith(dispatch, new sharedActions.CreateRoom({
           nameTemplate: "fakeText",
           roomOwner: fakeEmail
         }));
       });
@@ -798,17 +798,17 @@ describe("loop.panel", function() {
         callback({
           url: "http://invalid.com",
           description: "fakeSite",
           favicon: favicon,
           previews: ["fakeimage.png"]
         });
       };
 
-      var view = createTestComponent();
+      var view = createTestComponent(false);
 
       // Simulate being visible
       view.onDocumentVisible();
 
       var node = view.getDOMNode();
 
       // Select the checkbox
       TestUtils.Simulate.click(node.querySelector(".checkbox-wrapper"));
@@ -838,17 +838,17 @@ describe("loop.panel", function() {
       fakeMozLoop.getSelectedTabMetadata = function (callback) {
         callback({
           url: "https://www.example.com",
           description: "fake description",
           previews: [""]
         });
       };
 
-      var view = createTestComponent();
+      var view = createTestComponent(false);
 
       // Simulate being visible
       view.onDocumentVisible();
 
       var contextContent = view.getDOMNode().querySelector(".context-content");
       expect(contextContent).to.not.equal(null);
     });
 
@@ -856,17 +856,17 @@ describe("loop.panel", function() {
       fakeMozLoop.getSelectedTabMetadata = function (callback) {
         callback({
           url: "https://www.example.com",
           description: "fake description",
           previews: [""]
         });
       };
 
-      var view = createTestComponent();
+      var view = createTestComponent(false);
 
       view.setState({ checked: true });
 
       // Simulate being visible
       view.onDocumentVisible();
 
       expect(view.state.checked).eql(false);
     });
@@ -875,17 +875,17 @@ describe("loop.panel", function() {
       fakeMozLoop.getSelectedTabMetadata = function (callback) {
         callback({
           url: "https://www.example.com",
           description: "fake description",
           previews: [""]
         });
       };
 
-      var view = createTestComponent();
+      var view = createTestComponent(false);
 
       // Simulate being visible
       view.onDocumentVisible();
 
       var previewImage = view.getDOMNode().querySelector(".context-preview");
       expect(previewImage.src).to.match(/loop\/shared\/img\/icons-16x16.svg#globe$/);
     });
 
@@ -893,34 +893,34 @@ describe("loop.panel", function() {
       fakeMozLoop.getSelectedTabMetadata = function (callback) {
         callback({
           url: "",
           description: "fake description",
           previews: [""]
         });
       };
 
-      var view = createTestComponent();
+      var view = createTestComponent(false);
 
       view.onDocumentVisible();
 
       var contextInfo = view.getDOMNode().querySelector(".context");
       expect(contextInfo.classList.contains("hide")).to.equal(true);
     });
 
     it("should show only the hostname of the url", function() {
       fakeMozLoop.getSelectedTabMetadata = function (callback) {
         callback({
           url: "https://www.example.com:1234",
           description: "fake description",
           previews: [""]
         });
       };
 
-      var view = createTestComponent();
+      var view = createTestComponent(false);
 
       // Simulate being visible
       view.onDocumentVisible();
 
       var contextHostname = view.getDOMNode().querySelector(".context-url");
       expect(contextHostname.textContent).eql("www.example.com");
     });
 
@@ -930,17 +930,17 @@ describe("loop.panel", function() {
         callback({
           url: "https://www.example.com:1234",
           description: "fake description",
           favicon: favicon,
           previews: ["foo.gif"]
         });
       };
 
-      var view = createTestComponent();
+      var view = createTestComponent(false);
 
       // Simulate being visible.
       view.onDocumentVisible();
 
       var contextPreview = view.getDOMNode().querySelector(".context-preview");
       expect(contextPreview.src).eql(favicon);
     });
   });
--- a/browser/components/loop/test/desktop-local/roomViews_test.js
+++ b/browser/components/loop/test/desktop-local/roomViews_test.js
@@ -138,18 +138,19 @@ describe("loop.roomViews", function () {
       view = null;
     });
 
     function mountTestComponent(props) {
       props = _.extend({
         dispatcher: dispatcher,
         mozLoop: fakeMozLoop,
         roomData: {},
+        savingContext: false,
         show: true,
-        showContext: false
+        showEditContext: false
       }, props);
       return TestUtils.renderIntoDocument(
         React.createElement(loop.roomViews.DesktopRoomInvitationView, props));
     }
 
     it("should dispatch an EmailRoomUrl action when the email button is pressed",
       function() {
         view = mountTestComponent({
@@ -248,100 +249,34 @@ describe("loop.roomViews", 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("Context", function() {
-      it("should not render the context data when told not to", function() {
-        view = mountTestComponent();
-
-        expect(view.getDOMNode().querySelector(".room-context")).to.eql(null);
-      });
-
-      it("should render context when data is available", function() {
-        view = mountTestComponent({
-          showContext: true,
-          roomData: {
-            roomContextUrls: [fakeContextURL]
-          }
-        });
-
-        expect(view.getDOMNode().querySelector(".room-context")).to.not.eql(null);
-      });
-
-      it("should render the context in editMode when the pencil is clicked", function() {
-        view = mountTestComponent({
-          showContext: true,
-          roomData: {
-            roomContextUrls: [fakeContextURL]
-          }
-        });
-
-        var pencil = view.getDOMNode().querySelector(".room-context-btn-edit");
-        expect(pencil).to.not.eql(null);
-
-        React.addons.TestUtils.Simulate.click(pencil);
-
-        expect(view.state.editMode).to.eql(true);
-        var node = view.getDOMNode();
-        expect(node.querySelector("form")).to.not.eql(null);
-        // No text paragraphs should be visible in editMode.
-        var visiblePs = Array.slice(node.querySelector("p")).filter(function(p) {
-          return p.classList.contains("hide") || p.classList.contains("error");
-        });
-        expect(visiblePs.length).to.eql(0);
-      });
-
-      it("should format the context url for display", function() {
-        sandbox.stub(sharedUtils, "formatURL").returns({
-          location: "location",
-          hostname: "hostname"
-        });
-
-        view = mountTestComponent({
-          showContext: true,
-          roomData: {
-            roomContextUrls: [fakeContextURL]
-          }
-        });
-
-        expect(view.getDOMNode().querySelector(".room-context-url").textContent)
-          .eql("hostname");
-      });
-
-      it("should show a default favicon when none is available", function() {
-        fakeContextURL.thumbnail = null;
-        view = mountTestComponent({
-          showContext: true,
-          roomData: {
-            roomContextUrls: [fakeContextURL]
-          }
-        });
-
-        expect(view.getDOMNode().querySelector(".room-context-thumbnail").src)
-          .to.match(/loop\/shared\/img\/icons-16x16.svg#globe$/);
-      });
-    });
   });
 
   describe("DesktopRoomConversationView", function() {
     var view;
 
     beforeEach(function() {
       loop.store.StoreMixin.register({
         feedbackStore: new loop.store.FeedbackStore(dispatcher, {
           feedbackClient: {}
         })
       });
       sandbox.stub(dispatcher, "dispatch");
+      fakeMozLoop.getLoopPref = function(prefName) {
+        if (prefName == "contextInConversations.enabled") {
+          return true;
+        }
+        return "test";
+      };
     });
 
     function mountTestComponent() {
       return TestUtils.renderIntoDocument(
         React.createElement(loop.roomViews.DesktopRoomConversationView, {
           dispatcher: dispatcher,
           roomStore: roomStore,
           mozLoop: fakeMozLoop
@@ -742,17 +677,17 @@ describe("loop.roomViews", function () {
             provider: fakeProvider,
             roomUrl: "http://example.com",
             previews: []
           }));
       });
     });
   });
 
-  describe("DesktopRoomContextView", function() {
+  describe("DesktopRoomEditContextView", function() {
     var view;
 
     afterEach(function() {
       view = null;
     });
 
     function mountTestComponent(props) {
       props = _.extend({
@@ -760,45 +695,20 @@ describe("loop.roomViews", function () {
         mozLoop: fakeMozLoop,
         savingContext: false,
         show: true,
         roomData: {
           roomToken: "fakeToken"
         }
       }, props);
       return TestUtils.renderIntoDocument(
-        React.createElement(loop.roomViews.DesktopRoomContextView, props));
+        React.createElement(loop.roomViews.DesktopRoomEditContextView, props));
     }
 
     describe("#render", function() {
-      it("should show the context information properly when available", function() {
-        view = mountTestComponent({
-          roomData: {
-            roomDescription: "Hello, is it me you're looking for?",
-            roomContextUrls: [fakeContextURL]
-          }
-        });
-
-        var node = view.getDOMNode();
-        expect(node).to.not.eql(null);
-        expect(node.querySelector(".room-context-thumbnail").src).to.
-          eql(fakeContextURL.thumbnail);
-        expect(node.querySelector(".room-context-description").firstChild.textContent).
-          to.eql(fakeContextURL.description);
-      });
-
-      it("should not render optional data", function() {
-        view = mountTestComponent({
-          roomData: { roomContextUrls: [fakeContextURL] }
-        });
-
-        expect(view.getDOMNode().querySelector(".room-context-comment")).to.
-          eql(null);
-      });
-
       it("should not render the component when 'show' is false", function() {
         view = mountTestComponent({
           show: false
         });
 
         expect(view.getDOMNode()).to.eql(null);
       });
 
@@ -807,95 +717,60 @@ describe("loop.roomViews", function () {
           roomData: { roomContextUrls: [fakeContextURL] }
         });
 
         var closeBtn = view.getDOMNode().querySelector(".room-context-btn-close");
         React.addons.TestUtils.Simulate.click(closeBtn);
         expect(view.getDOMNode()).to.eql(null);
       });
 
-      it("should render the view in editMode when appropriate", function() {
+      it("should render the view correctly", function() {
         var roomName = "Hello, is it me you're looking for?";
         view = mountTestComponent({
-          editMode: true,
           roomData: {
             roomName: roomName,
             roomContextUrls: [fakeContextURL]
           }
         });
 
         var node = view.getDOMNode();
         expect(node.querySelector("form")).to.not.eql(null);
         // Check the contents of the form fields.
         expect(node.querySelector(".room-context-name").value).to.eql(roomName);
         expect(node.querySelector(".room-context-url").value).to.eql(fakeContextURL.location);
         expect(node.querySelector(".room-context-comments").value).to.eql(fakeContextURL.description);
       });
 
       it("should show the checkbox as disabled when context is already set", function() {
         view = mountTestComponent({
-          editMode: true,
           roomData: {
             roomToken: "fakeToken",
             roomName: "fakeName",
             roomContextUrls: [fakeContextURL]
           }
         });
 
         var checkbox = view.getDOMNode().querySelector(".checkbox");
         expect(checkbox.classList.contains("disabled")).to.eql(true);
       });
 
-      it("should render the editMode view when the edit button is clicked", function(done) {
-        var roomName = "Hello, is it me you're looking for?";
-        view = mountTestComponent({
-          roomData: {
-            roomToken: "fakeToken",
-            roomName: roomName,
-            roomContextUrls: [fakeContextURL]
-          }
-        });
-
-        // Switch to editMode via setting the prop, since we can control that
-        // better.
-        view.setProps({ editMode: true }, function() {
-          // First check if availableContext is set correctly.
-          expect(view.state.availableContext).to.not.eql(null);
-          expect(view.state.availableContext.previewImage).to.eql(favicon);
-
-          var node = view.getDOMNode();
-          expect(node.querySelector(".checkbox-wrapper").classList.contains("disabled")).to.eql(true);
-          expect(node.querySelector(".room-context-name").value).to.eql(roomName);
-          expect(node.querySelector(".room-context-url").value).to.eql(fakeContextURL.location);
-          expect(node.querySelector(".room-context-comments").value).to.eql(fakeContextURL.description);
-
-          done();
-        });
-      });
-
-      it("should hide the checkbox when no context data is stored or available", function(done) {
+      it("should hide the checkbox when no context data is stored or available", function() {
         view = mountTestComponent({
           roomData: {
             roomToken: "fakeToken",
             roomName: "Hello, is it me you're looking for?"
           }
         });
 
-        // Switch to editMode via setting the prop, since we can control that
-        // better.
-        view.setProps({ editMode: true }, function() {
-          // First check if availableContext is set correctly.
-          expect(view.state.availableContext).to.not.eql(null);
-          expect(view.state.availableContext.previewImage).to.eql(favicon);
+        // First check if availableContext is set correctly.
+        expect(view.state.availableContext).to.not.eql(null);
+        expect(view.state.availableContext.previewImage).to.eql(favicon);
 
-          var node = view.getDOMNode();
-          expect(node.querySelector(".checkbox-wrapper").classList.contains("hide")).to.eql(true);
-
-          done();
-        });
+        var node = view.getDOMNode();
+        expect(node.querySelector(".checkbox-wrapper").classList.contains("hide")).to.eql(true);
       });
     });
 
     describe("Update Room", function() {
       var roomNameBox;
 
       beforeEach(function() {
         sandbox.stub(dispatcher, "dispatch");
@@ -953,35 +828,34 @@ describe("loop.roomViews", function () {
       it("should close the edit form when context was saved successfully", function(done) {
         view.setProps({ savingContext: true }, function() {
           var node = view.getDOMNode();
           // The button should show up as disabled.
           expect(node.querySelector(".btn-info").hasAttribute("disabled")).to.eql(true);
 
           // Now simulate a successful save.
           view.setProps({ savingContext: false }, function() {
-            // The editMode flag should be updated.
-            expect(view.state.editMode).to.eql(false);
+            // The 'show flag should be updated.
+            expect(view.state.show).to.eql(false);
             done();
           });
         });
       });
     });
 
     describe("#handleCheckboxChange", function() {
       var node, checkbox;
 
       beforeEach(function() {
+        fakeMozLoop.getSelectedTabMetadata = sinon.stub().callsArgWith(0, {
+          favicon: fakeContextURL.thumbnail,
+          title: fakeContextURL.description,
+          url: fakeContextURL.location
+        });
         view = mountTestComponent({
-          availableContext: {
-            description: fakeContextURL.description,
-            previewImage: fakeContextURL.thumbnail,
-            url: fakeContextURL.location
-          },
-          editMode: true,
           roomData: {
             roomToken: "fakeToken",
             roomName: "fakeName"
           }
         });
 
         node = view.getDOMNode();
         checkbox = node.querySelector(".checkbox");
--- a/browser/components/loop/test/shared/textChatView_test.js
+++ b/browser/components/loop/test/shared/textChatView_test.js
@@ -38,34 +38,37 @@ describe("loop.shared.views.TextChatView
   });
 
   describe("TextChatEntriesView", function() {
     var view;
 
     function mountTestComponent(extraProps) {
       var basicProps = {
         dispatcher: dispatcher,
-        messageList: []
+        messageList: [],
+        useDesktopPaths: false
       };
 
       return TestUtils.renderIntoDocument(
         React.createElement(loop.shared.views.chat.TextChatEntriesView,
           _.extend(basicProps, extraProps)));
     }
 
     it("should render message entries when message were sent/ received", function() {
       view = mountTestComponent({
         messageList: [{
           type: CHAT_MESSAGE_TYPES.RECEIVED,
           contentType: CHAT_CONTENT_TYPES.TEXT,
-          message: "Hello!"
+          message: "Hello!",
+          receivedTimestamp: "2015-06-25T17:53:55.357Z"
         }, {
           type: CHAT_MESSAGE_TYPES.SENT,
           contentType: CHAT_CONTENT_TYPES.TEXT,
-          message: "Is it me you're looking for?"
+          message: "Is it me you're looking for?",
+          sentTimestamp: "2015-06-25T17:53:55.357Z"
         }]
       });
 
       var node = view.getDOMNode();
       expect(node).to.not.eql(null);
 
       var entries = node.querySelectorAll(".text-chat-entry");
       expect(entries.length).to.eql(2);
@@ -76,112 +79,121 @@ describe("loop.shared.views.TextChatView
     it("should play a sound when a message is received", function() {
       view = mountTestComponent();
       sandbox.stub(view, "play");
 
       view.setProps({
         messageList: [{
           type: CHAT_MESSAGE_TYPES.RECEIVED,
           contentType: CHAT_CONTENT_TYPES.TEXT,
-          message: "Hello!"
+          message: "Hello!",
+          receivedTimestamp: "2015-06-25T17:53:55.357Z"
         }]
       });
 
       sinon.assert.calledOnce(view.play);
       sinon.assert.calledWithExactly(view.play, "message");
     });
 
     it("should not play a sound when a special message is displayed", function() {
       view = mountTestComponent();
       sandbox.stub(view, "play");
 
       view.setProps({
         messageList: [{
           type: CHAT_MESSAGE_TYPES.SPECIAL,
           contentType: CHAT_CONTENT_TYPES.ROOM_NAME,
-          message: "Hello!"
+          message: "Hello!",
+          receivedTimestamp: "2015-06-25T17:53:55.357Z"
         }]
       });
 
       sinon.assert.notCalled(view.play);
     });
 
     it("should not play a sound when a message is sent", function() {
       view = mountTestComponent();
       sandbox.stub(view, "play");
 
       view.setProps({
         messageList: [{
           type: CHAT_MESSAGE_TYPES.SENT,
           contentType: CHAT_CONTENT_TYPES.TEXT,
-          message: "Hello!"
+          message: "Hello!",
+          sentTimestamp: "2015-06-25T17:53:55.357Z"
         }]
       });
 
       sinon.assert.notCalled(view.play);
     });
   });
 
   describe("TextChatEntry", function() {
     var view;
 
     function mountTestComponent(extraProps) {
       var props = _.extend({
-        dispatcher: dispatcher
+        contentType: CHAT_CONTENT_TYPES.TEXT,
+        dispatcher: dispatcher,
+        message: "test",
+        type: CHAT_MESSAGE_TYPES.RECEIVED,
+        timestamp: "2015-06-23T22:48:39.738Z"
       }, extraProps);
       return TestUtils.renderIntoDocument(
         React.createElement(loop.shared.views.chat.TextChatEntry, props));
     }
 
     it("should not render a timestamp", function() {
       view = mountTestComponent({
-        showTimestamp: false,
-        timestamp: "2015-06-23T22:48:39.738Z"
+        showTimestamp: false
       });
       var node = view.getDOMNode();
 
       expect(node.querySelector(".text-chat-entry-timestamp")).to.eql(null);
     });
 
     it("should render a timestamp", function() {
       view = mountTestComponent({
-        showTimestamp: true,
-        timestamp: "2015-06-23T22:48:39.738Z"
+        showTimestamp: true
       });
       var node = view.getDOMNode();
 
       expect(node.querySelector(".text-chat-entry-timestamp")).to.not.eql(null);
     });
   });
 
   describe("TextChatEntriesView", function() {
     var view, node;
 
     function mountTestComponent(extraProps) {
       var props = _.extend({
-        dispatcher: dispatcher
+        dispatcher: dispatcher,
+        messageList: [],
+        useDesktopPaths: false
       }, extraProps);
       return TestUtils.renderIntoDocument(
         React.createElement(loop.shared.views.chat.TextChatEntriesView, props));
     }
 
     beforeEach(function() {
       store.setStoreState({ textChatEnabled: true });
     });
 
     it("should show timestamps if there are different senders", function() {
       view = mountTestComponent({
         messageList: [{
           type: CHAT_MESSAGE_TYPES.RECEIVED,
           contentType: CHAT_CONTENT_TYPES.TEXT,
-          message: "Hello!"
+          message: "Hello!",
+          receivedTimestamp: "2015-06-25T17:53:55.357Z"
         }, {
           type: CHAT_MESSAGE_TYPES.SENT,
           contentType: CHAT_CONTENT_TYPES.TEXT,
-          message: "Is it me you're looking for?"
+          message: "Is it me you're looking for?",
+          sentTimestamp: "2015-06-25T17:53:55.357Z"
         }]
       });
       node = view.getDOMNode();
 
       expect(node.querySelectorAll(".text-chat-entry-timestamp").length)
           .to.eql(2);
     });
 
@@ -225,36 +237,39 @@ describe("loop.shared.views.TextChatView
           .to.eql(2);
     });
 
     it("should not show timestamps from msgs sent in the same minute", function() {
       view = mountTestComponent({
         messageList: [{
           type: CHAT_MESSAGE_TYPES.RECEIVED,
           contentType: CHAT_CONTENT_TYPES.TEXT,
-          message: "Hello!"
+          message: "Hello!",
+          receivedTimestamp: "2015-06-25T17:53:55.357Z"
         }, {
           type: CHAT_MESSAGE_TYPES.RECEIVED,
           contentType: CHAT_CONTENT_TYPES.TEXT,
-          message: "Is it me you're looking for?"
+          message: "Is it me you're looking for?",
+          sentTimestamp: "2015-06-25T17:53:55.357Z"
         }]
       });
       node = view.getDOMNode();
 
       expect(node.querySelectorAll(".text-chat-entry-timestamp").length)
           .to.eql(1);
     });
   });
 
   describe("TextChatView", function() {
     var view;
 
     function mountTestComponent(extraProps) {
       var props = _.extend({
         dispatcher: dispatcher,
+        showRoomName: false,
         useDesktopPaths: false
       }, extraProps);
       return TestUtils.renderIntoDocument(
         React.createElement(loop.shared.views.chat.TextChatView, props));
     }
 
     beforeEach(function() {
       store.setStoreState({ textChatEnabled: true });
@@ -284,25 +299,25 @@ describe("loop.shared.views.TextChatView
       });
 
       var node = view.getDOMNode();
 
       expect(node.querySelectorAll(".text-chat-entry-timestamp").length)
           .to.eql(2);
     });
 
-    it("should display the view if no messages and text chat not enabled", function() {
+    it("should not display the view if no messages and text chat not enabled", function() {
       store.setStoreState({ textChatEnabled: false });
 
       view = mountTestComponent();
 
-      expect(view.getDOMNode()).not.eql(null);
+      expect(view.getDOMNode()).eql(null);
     });
 
-    it("should display the view if text chat is enabled", function() {
+    it("should display the view if no messages and text chat is enabled", function() {
       view = mountTestComponent();
 
       expect(view.getDOMNode()).not.eql(null);
     });
 
     it("should display only the text chat box if entry is enabled but there are no messages", function() {
       view = mountTestComponent();
 
@@ -312,21 +327,25 @@ describe("loop.shared.views.TextChatView
       expect(node.querySelector(".text-chat-entries")).eql(null);
     });
 
     it("should render message entries when message were sent/ received", function() {
       view = mountTestComponent();
 
       store.receivedTextChatMessage({
         contentType: CHAT_CONTENT_TYPES.TEXT,
-        message: "Hello!"
+        message: "Hello!",
+        sentTimestamp: "1970-01-01T00:03:00.000Z",
+        receivedTimestamp: "1970-01-01T00:03:00.000Z"
       });
       store.sendTextChatMessage({
         contentType: CHAT_CONTENT_TYPES.TEXT,
-        message: "Is it me you're looking for?"
+        message: "Is it me you're looking for?",
+        sentTimestamp: "1970-01-01T00:03:00.000Z",
+        receivedTimestamp: "1970-01-01T00:03:00.000Z"
       });
 
       var node = view.getDOMNode();
       expect(node.querySelector(".text-chat-entries")).to.not.eql(null);
 
       var entries = node.querySelectorAll(".text-chat-entry");
       expect(entries.length).to.eql(2);
       expect(entries[0].classList.contains("received")).to.eql(true);
@@ -341,27 +360,28 @@ describe("loop.shared.views.TextChatView
         message: "Foo",
         timestamp: 0
       });
 
       expect(node.querySelector(".sent")).to.not.eql(null);
     });
 
     it("should add `received` CSS class selector to msg of type RECEIVED",
-       function() {
-         var node = mountTestComponent().getDOMNode();
+      function() {
+        var node = mountTestComponent().getDOMNode();
 
-         store.receivedTextChatMessage({
-           contentType: CHAT_CONTENT_TYPES.TEXT,
-           message: "Foo",
-           timestamp: 0
-         });
+        store.receivedTextChatMessage({
+          contentType: CHAT_CONTENT_TYPES.TEXT,
+          message: "Foo",
+          sentTimestamp: "1970-01-01T00:03:00.000Z",
+          receivedTimestamp: "1970-01-01T00:03:00.000Z"
+        });
 
-         expect(node.querySelector(".received")).to.not.eql(null);
-     });
+        expect(node.querySelector(".received")).to.not.eql(null);
+      });
 
     it("should render a room name special entry", function() {
       view = mountTestComponent({
         showRoomName: true
       });
 
       store.updateRoomInfo(new sharedActions.UpdateRoomInfo({
         roomName: "A wonderful surprise!",
--- a/browser/components/loop/test/shared/views_test.js
+++ b/browser/components/loop/test/shared/views_test.js
@@ -251,16 +251,19 @@ describe("loop.shared.views", function()
           new sharedActions.EndScreenShare({}));
       });
   });
 
   describe("ConversationToolbar", function() {
     var hangup, publishStream;
 
     function mountTestComponent(props) {
+      props = _.extend({
+        dispatcher: dispatcher
+      }, props || {});
       return TestUtils.renderIntoDocument(
         React.createElement(sharedViews.ConversationToolbar, props));
     }
 
     beforeEach(function() {
       hangup = sandbox.stub();
       publishStream = sandbox.stub();
     });
@@ -357,16 +360,19 @@ describe("loop.shared.views", function()
       sinon.assert.calledWithExactly(publishStream, "video", true);
     });
   });
 
   describe("ConversationView", function() {
     var fakeSDK, fakeSessionData, fakeSession, fakePublisher, model, fakeAudio;
 
     function mountTestComponent(props) {
+      props = _.extend({
+        dispatcher: dispatcher
+      }, props || {});
       return TestUtils.renderIntoDocument(
         React.createElement(sharedViews.ConversationView, props));
     }
 
     beforeEach(function() {
       fakeAudio = {
         play: sinon.spy(),
         pause: sinon.spy(),
@@ -680,16 +686,19 @@ describe("loop.shared.views", function()
       });
     });
   });
 
   describe("NotificationListView", function() {
     var coll, view, testNotif;
 
     function mountTestComponent(props) {
+      props = _.extend({
+        key: 0
+      }, props || {});
       return TestUtils.renderIntoDocument(
         React.createElement(sharedViews.NotificationListView, props));
     }
 
     beforeEach(function() {
       coll = new sharedModels.NotificationCollection();
       view = mountTestComponent({notifications: coll});
       testNotif = {level: "warning", message: "foo"};
@@ -835,17 +844,21 @@ describe("loop.shared.views", function()
     });
   });
 
   describe("ContextUrlView", function() {
     var view;
 
     function mountTestComponent(extraProps) {
       var props = _.extend({
-        dispatcher: dispatcher
+        allowClick: false,
+        description: "test",
+        dispatcher: dispatcher,
+        showContextTitle: false,
+        useDesktopPaths: false
       }, extraProps);
       return TestUtils.renderIntoDocument(
         React.createElement(sharedViews.ContextUrlView, props));
     }
 
     it("should display nothing if the url is invalid", function() {
       view = mountTestComponent({
         url: "fjrTykyw"
@@ -917,16 +930,19 @@ describe("loop.shared.views", function()
         }));
     });
   });
 
   describe("MediaView", function() {
     var view;
 
     function mountTestComponent(props) {
+      props = _.extend({
+        isLoading: false
+      }, props || {});
       return TestUtils.renderIntoDocument(
         React.createElement(sharedViews.MediaView, props));
     }
 
     it("should display an avatar view", function() {
       view = mountTestComponent({
         displayAvatar: true,
         mediaType: "local"