mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 12:37:37 +00:00
Bug 1147609 - Make Loop's standalone UI work with roomName as an unecrypted parameter or as an encrypted part of context. r=mikedeboer
This commit is contained in:
parent
8769806b40
commit
a68bae1728
@ -972,11 +972,14 @@ html, .fx-embedded, #main,
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.standalone .room-conversation h2.room-name {
|
||||
.standalone .room-conversation h2.room-name,
|
||||
.standalone .room-conversation h2.room-info-failure {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
top: 0;
|
||||
right: 0;
|
||||
right: 10px;
|
||||
/* 20px is 10px for left and right margins. */
|
||||
width: calc(25% - 20px);
|
||||
color: #fff;
|
||||
z-index: 2000000;
|
||||
font-size: 1.2em;
|
||||
|
@ -43,7 +43,8 @@ loop.shared.actions = (function() {
|
||||
* Extract the token information and type for the standalone window
|
||||
*/
|
||||
ExtractTokenInfo: Action.define("extractTokenInfo", {
|
||||
windowPath: String
|
||||
windowPath: String,
|
||||
windowHash: String
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -65,6 +66,7 @@ loop.shared.actions = (function() {
|
||||
* token.
|
||||
*/
|
||||
FetchServerData: Action.define("fetchServerData", {
|
||||
// cryptoKey: String - Optional.
|
||||
token: String,
|
||||
windowType: String
|
||||
}),
|
||||
@ -386,6 +388,7 @@ loop.shared.actions = (function() {
|
||||
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
|
||||
*/
|
||||
UpdateRoomInfo: Action.define("updateRoomInfo", {
|
||||
// context: Object - Optional.
|
||||
// roomName: String - Optional.
|
||||
roomOwner: String,
|
||||
roomUrl: String
|
||||
|
@ -11,6 +11,7 @@ loop.store.ActiveRoomStore = (function() {
|
||||
"use strict";
|
||||
|
||||
var sharedActions = loop.shared.actions;
|
||||
var crypto = loop.crypto;
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
|
||||
@ -20,6 +21,8 @@ loop.store.ActiveRoomStore = (function() {
|
||||
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
|
||||
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
|
||||
|
||||
/**
|
||||
* Active room store.
|
||||
*
|
||||
@ -76,7 +79,11 @@ loop.store.ActiveRoomStore = (function() {
|
||||
localVideoDimensions: {},
|
||||
remoteVideoDimensions: {},
|
||||
screenSharingState: SCREEN_SHARE_STATES.INACTIVE,
|
||||
receivingScreenShare: false
|
||||
receivingScreenShare: false,
|
||||
// The roomCryptoKey to decode the context data if necessary.
|
||||
roomCryptoKey: null,
|
||||
// Room information failed to be obtained for a reason. See ROOM_INFO_FAILURES.
|
||||
roomInfoFailure: null
|
||||
};
|
||||
},
|
||||
|
||||
@ -199,6 +206,7 @@ loop.store.ActiveRoomStore = (function() {
|
||||
|
||||
this.setStoreState({
|
||||
roomToken: actionData.token,
|
||||
roomCryptoKey: actionData.cryptoKey,
|
||||
roomState: ROOM_STATES.READY
|
||||
});
|
||||
|
||||
@ -207,17 +215,64 @@ loop.store.ActiveRoomStore = (function() {
|
||||
this._mozLoop.rooms.on("delete:" + actionData.roomToken,
|
||||
this._handleRoomDelete.bind(this));
|
||||
|
||||
this._mozLoop.rooms.get(this._storeState.roomToken,
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
// XXX Bug 1110937 will want to handle the error results here
|
||||
// e.g. room expired/invalid.
|
||||
console.error("Failed to get room data:", err);
|
||||
return;
|
||||
}
|
||||
this._getRoomDataForStandalone();
|
||||
},
|
||||
|
||||
this.dispatcher.dispatch(new sharedActions.UpdateRoomInfo(result));
|
||||
}.bind(this));
|
||||
_getRoomDataForStandalone: function() {
|
||||
this._mozLoop.rooms.get(this._storeState.roomToken, function(err, result) {
|
||||
if (err) {
|
||||
// XXX Bug 1110937 will want to handle the error results here
|
||||
// e.g. room expired/invalid.
|
||||
console.error("Failed to get room data:", err);
|
||||
return;
|
||||
}
|
||||
|
||||
var roomInfoData = new sharedActions.UpdateRoomInfo({
|
||||
roomOwner: result.roomOwner,
|
||||
roomUrl: result.roomUrl
|
||||
});
|
||||
|
||||
if (!result.context && !result.roomName) {
|
||||
roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.NO_DATA;
|
||||
this.dispatcher.dispatch(roomInfoData);
|
||||
return;
|
||||
}
|
||||
|
||||
// This handles 'legacy', non-encrypted room names.
|
||||
if (result.roomName && !result.context) {
|
||||
roomInfoData.roomName = result.roomName;
|
||||
this.dispatcher.dispatch(roomInfoData);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!crypto.isSupported()) {
|
||||
roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED;
|
||||
this.dispatcher.dispatch(roomInfoData);
|
||||
return;
|
||||
}
|
||||
|
||||
var roomCryptoKey = this.getStoreState("roomCryptoKey");
|
||||
|
||||
if (!roomCryptoKey) {
|
||||
roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.NO_CRYPTO_KEY;
|
||||
this.dispatcher.dispatch(roomInfoData);
|
||||
return;
|
||||
}
|
||||
|
||||
var dispatcher = this.dispatcher;
|
||||
|
||||
crypto.decryptBytes(roomCryptoKey, result.context.value)
|
||||
.then(function(decryptedResult) {
|
||||
var realResult = JSON.parse(decryptedResult);
|
||||
|
||||
roomInfoData.roomName = realResult.roomName;
|
||||
|
||||
dispatcher.dispatch(roomInfoData);
|
||||
}, function(err) {
|
||||
roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.DECRYPT_FAILED;
|
||||
dispatcher.dispatch(roomInfoData);
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
@ -254,6 +309,7 @@ loop.store.ActiveRoomStore = (function() {
|
||||
*/
|
||||
updateRoomInfo: function(actionData) {
|
||||
this.setStoreState({
|
||||
roomInfoFailure: actionData.roomInfoFailure,
|
||||
roomName: actionData.roomName,
|
||||
roomOwner: actionData.roomOwner,
|
||||
roomUrl: actionData.roomUrl
|
||||
|
@ -43,6 +43,17 @@ loop.shared.utils = (function(mozL10n) {
|
||||
UNKNOWN: "reason-unknown"
|
||||
};
|
||||
|
||||
var ROOM_INFO_FAILURES = {
|
||||
// There's no data available from the server.
|
||||
NO_DATA: "no_data",
|
||||
// WebCrypto is unsupported in this browser.
|
||||
WEB_CRYPTO_UNSUPPORTED: "web_crypto_unsupported",
|
||||
// The room is missing the crypto key information.
|
||||
NO_CRYPTO_KEY: "no_crypto_key",
|
||||
// Decryption failed.
|
||||
DECRYPT_FAILED: "decrypt_failed"
|
||||
};
|
||||
|
||||
var STREAM_PROPERTIES = {
|
||||
VIDEO_DIMENSIONS: "videoDimensions",
|
||||
HAS_AUDIO: "hasAudio",
|
||||
@ -397,6 +408,7 @@ loop.shared.utils = (function(mozL10n) {
|
||||
WEBSOCKET_REASONS: WEBSOCKET_REASONS,
|
||||
STREAM_PROPERTIES: STREAM_PROPERTIES,
|
||||
SCREEN_SHARE_STATES: SCREEN_SHARE_STATES,
|
||||
ROOM_INFO_FAILURES: ROOM_INFO_FAILURES,
|
||||
composeCallUrlEmail: composeCallUrlEmail,
|
||||
formatDate: formatDate,
|
||||
getBoolPreference: getBoolPreference,
|
||||
|
@ -108,6 +108,7 @@
|
||||
<!-- app scripts -->
|
||||
<script type="text/javascript" src="config.js"></script>
|
||||
<script type="text/javascript" src="shared/js/utils.js"></script>
|
||||
<script type="text/javascript" src="shared/js/crypto.js"></script>
|
||||
<script type="text/javascript" src="shared/js/models.js"></script>
|
||||
<script type="text/javascript" src="shared/js/mixins.js"></script>
|
||||
<script type="text/javascript" src="shared/js/feedbackApiClient.js"></script>
|
||||
|
@ -95,9 +95,21 @@ loop.store.StandaloneAppStore = (function() {
|
||||
return [windowType, match && match[1] ? match[1] : null];
|
||||
},
|
||||
|
||||
/**
|
||||
* Extracts the crypto key from the hash for the page.
|
||||
*/
|
||||
_extractCryptoKey: function(windowHash) {
|
||||
if (windowHash && windowHash[0] === "#") {
|
||||
return windowHash.substring(1, windowHash.length);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the extract token info action - obtains the token information
|
||||
* and its type; updates the store and notifies interested components.
|
||||
* and its type; extracts any crypto information; updates the store and
|
||||
* notifies interested components.
|
||||
*
|
||||
* @param {sharedActions.GetWindowData} actionData The action data
|
||||
*/
|
||||
@ -135,6 +147,7 @@ loop.store.StandaloneAppStore = (function() {
|
||||
// it.
|
||||
if (token) {
|
||||
this._dispatcher.dispatch(new loop.shared.actions.FetchServerData({
|
||||
cryptoKey: this._extractCryptoKey(actionData.windowHash),
|
||||
token: token,
|
||||
windowType: windowType
|
||||
}));
|
||||
|
@ -12,6 +12,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
"use strict";
|
||||
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
@ -198,6 +199,29 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomContextView = React.createClass({displayName: "StandaloneRoomContextView",
|
||||
propTypes: {
|
||||
roomName: React.PropTypes.string,
|
||||
roomInfoFailure: React.PropTypes.string
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.props.roomInfoFailure === ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED) {
|
||||
return (React.createElement("h2", {className: "room-info-failure"},
|
||||
mozL10n.get("room_information_failure_unsupported_browser")
|
||||
));
|
||||
} else if (this.props.roomInfoFailure) {
|
||||
return (React.createElement("h2", {className: "room-info-failure"},
|
||||
mozL10n.get("room_information_failure_not_available")
|
||||
));
|
||||
}
|
||||
|
||||
return (
|
||||
React.createElement("h2", {className: "room-name"}, this.props.roomName)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomView = React.createClass({displayName: "StandaloneRoomView",
|
||||
mixins: [
|
||||
Backbone.Events,
|
||||
@ -458,7 +482,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
roomUsed: this.state.used}),
|
||||
React.createElement("div", {className: "video-layout-wrapper"},
|
||||
React.createElement("div", {className: "conversation room-conversation"},
|
||||
React.createElement("h2", {className: "room-name"}, this.state.roomName),
|
||||
React.createElement(StandaloneRoomContextView, {roomName: this.state.roomName,
|
||||
roomInfoFailure: this.state.roomInfoFailure}),
|
||||
React.createElement("div", {className: "media nested"},
|
||||
React.createElement("span", {className: "self-view-hidden-message"},
|
||||
mozL10n.get("self_view_hidden_message")
|
||||
@ -491,6 +516,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
});
|
||||
|
||||
return {
|
||||
StandaloneRoomContextView: StandaloneRoomContextView,
|
||||
StandaloneRoomView: StandaloneRoomView
|
||||
};
|
||||
})(navigator.mozL10n);
|
||||
|
@ -12,6 +12,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
"use strict";
|
||||
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
@ -198,6 +199,29 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomContextView = React.createClass({
|
||||
propTypes: {
|
||||
roomName: React.PropTypes.string,
|
||||
roomInfoFailure: React.PropTypes.string
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.props.roomInfoFailure === ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED) {
|
||||
return (<h2 className="room-info-failure">
|
||||
{mozL10n.get("room_information_failure_unsupported_browser")}
|
||||
</h2>);
|
||||
} else if (this.props.roomInfoFailure) {
|
||||
return (<h2 className="room-info-failure">
|
||||
{mozL10n.get("room_information_failure_not_available")}
|
||||
</h2>);
|
||||
}
|
||||
|
||||
return (
|
||||
<h2 className="room-name">{this.props.roomName}</h2>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var StandaloneRoomView = React.createClass({
|
||||
mixins: [
|
||||
Backbone.Events,
|
||||
@ -458,7 +482,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
roomUsed={this.state.used} />
|
||||
<div className="video-layout-wrapper">
|
||||
<div className="conversation room-conversation">
|
||||
<h2 className="room-name">{this.state.roomName}</h2>
|
||||
<StandaloneRoomContextView roomName={this.state.roomName}
|
||||
roomInfoFailure={this.state.roomInfoFailure} />
|
||||
<div className="media nested">
|
||||
<span className="self-view-hidden-message">
|
||||
{mozL10n.get("self_view_hidden_message")}
|
||||
@ -491,6 +516,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
});
|
||||
|
||||
return {
|
||||
StandaloneRoomContextView: StandaloneRoomContextView,
|
||||
StandaloneRoomView: StandaloneRoomView
|
||||
};
|
||||
})(navigator.mozL10n);
|
||||
|
@ -1109,7 +1109,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var locationData = sharedUtils.locationData();
|
||||
|
||||
dispatcher.dispatch(new sharedActions.ExtractTokenInfo({
|
||||
windowPath: locationData.pathname
|
||||
windowPath: locationData.pathname,
|
||||
windowHash: locationData.hash
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -1109,7 +1109,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var locationData = sharedUtils.locationData();
|
||||
|
||||
dispatcher.dispatch(new sharedActions.ExtractTokenInfo({
|
||||
windowPath: locationData.pathname
|
||||
windowPath: locationData.pathname,
|
||||
windowHash: locationData.hash
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -127,6 +127,8 @@ rooms_room_join_label=Join the conversation
|
||||
rooms_display_name_guest=Guest
|
||||
rooms_unavailable_notification_message=Sorry, you cannot join this conversation. The link may be expired or invalid.
|
||||
rooms_media_denied_message=We could not get access to your microphone or camera. Please reload the page to try again.
|
||||
room_information_failure_not_available=No information about this conversation is available. Please request a new link from the person who sent it to you.
|
||||
room_information_failure_unsupported_browser=Your browser cannot access any information about this conversation. Please make sure you're using the latest version.
|
||||
|
||||
## LOCALIZATION_NOTE(standalone_title_with_status): {{clientShortname}} will be
|
||||
## replaced by the brand name and {{currentStatus}} will be replaced
|
||||
|
@ -10,6 +10,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
|
||||
var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver;
|
||||
var fakeMultiplexGum;
|
||||
|
||||
@ -347,20 +348,127 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
sinon.assert.calledOnce(fakeMozLoop.rooms.get);
|
||||
});
|
||||
|
||||
it("should dispatch UpdateRoomInfo if mozLoop.rooms.get is successful", function() {
|
||||
var roomDetails = {
|
||||
roomName: "fakeName",
|
||||
roomUrl: "http://invalid",
|
||||
roomOwner: "gavin"
|
||||
};
|
||||
|
||||
fakeMozLoop.rooms.get.callsArgWith(1, null, roomDetails);
|
||||
it("should dispatch an UpdateRoomInfo message with 'no data' failure if neither roomName nor context are supplied", function() {
|
||||
fakeMozLoop.rooms.get.callsArgWith(1, null, {
|
||||
roomOwner: "Dan",
|
||||
roomUrl: "http://invalid"
|
||||
});
|
||||
|
||||
store.fetchServerData(fetchServerAction);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo(roomDetails));
|
||||
new sharedActions.UpdateRoomInfo({
|
||||
roomInfoFailure: ROOM_INFO_FAILURES.NO_DATA,
|
||||
roomOwner: "Dan",
|
||||
roomUrl: "http://invalid"
|
||||
}));
|
||||
});
|
||||
|
||||
describe("mozLoop.rooms.get returns roomName as a separate field (no context)", function() {
|
||||
it("should dispatch UpdateRoomInfo if mozLoop.rooms.get is successful", function() {
|
||||
var roomDetails = {
|
||||
roomName: "fakeName",
|
||||
roomUrl: "http://invalid",
|
||||
roomOwner: "gavin"
|
||||
};
|
||||
|
||||
fakeMozLoop.rooms.get.callsArgWith(1, null, roomDetails);
|
||||
|
||||
store.fetchServerData(fetchServerAction);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo(roomDetails));
|
||||
});
|
||||
});
|
||||
|
||||
describe("mozLoop.rooms.get returns encryptedContext", function() {
|
||||
var roomDetails, expectedDetails;
|
||||
|
||||
beforeEach(function() {
|
||||
roomDetails = {
|
||||
context: {
|
||||
value: "fakeContext"
|
||||
},
|
||||
roomUrl: "http://invalid",
|
||||
roomOwner: "Mark"
|
||||
};
|
||||
expectedDetails = {
|
||||
roomUrl: "http://invalid",
|
||||
roomOwner: "Mark"
|
||||
};
|
||||
|
||||
fakeMozLoop.rooms.get.callsArgWith(1, null, roomDetails);
|
||||
|
||||
sandbox.stub(loop.crypto, "isSupported").returns(true);
|
||||
});
|
||||
|
||||
it("should dispatch UpdateRoomInfo message with 'unsupported' failure if WebCrypto is unsupported", function() {
|
||||
loop.crypto.isSupported.returns(false);
|
||||
|
||||
store.fetchServerData(fetchServerAction);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo(_.extend({
|
||||
roomInfoFailure: ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED
|
||||
}, expectedDetails)));
|
||||
});
|
||||
|
||||
it("should dispatch UpdateRoomInfo message with 'no crypto key' failure if there is no crypto key", function() {
|
||||
store.fetchServerData(fetchServerAction);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo(_.extend({
|
||||
roomInfoFailure: ROOM_INFO_FAILURES.NO_CRYPTO_KEY
|
||||
}, expectedDetails)));
|
||||
});
|
||||
|
||||
it("should dispatch UpdateRoomInfo message with 'decrypt failed' failure if decryption failed", function() {
|
||||
fetchServerAction.cryptoKey = "fakeKey";
|
||||
|
||||
// This is a work around to turn promise into a sync action to make handling test failures
|
||||
// easier.
|
||||
sandbox.stub(loop.crypto, "decryptBytes", function() {
|
||||
return {
|
||||
then: function(resolve, reject) {
|
||||
reject(new Error("Operation unsupported"));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
store.fetchServerData(fetchServerAction);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo(_.extend({
|
||||
roomInfoFailure: ROOM_INFO_FAILURES.DECRYPT_FAILED
|
||||
}, expectedDetails)));
|
||||
});
|
||||
|
||||
it("should dispatch UpdateRoomInfo message with the room name if decryption was successful", function() {
|
||||
fetchServerAction.cryptoKey = "fakeKey";
|
||||
|
||||
// This is a work around to turn promise into a sync action to make handling test failures
|
||||
// easier.
|
||||
sandbox.stub(loop.crypto, "decryptBytes", function() {
|
||||
return {
|
||||
then: function(resolve, reject) {
|
||||
resolve(JSON.stringify({roomName: "The wonderful Loopy room"}));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
store.fetchServerData(fetchServerAction);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo(_.extend({
|
||||
roomName: "The wonderful Loopy room"
|
||||
}, expectedDetails)));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -54,7 +54,8 @@ describe("loop.store.StandaloneAppStore", function () {
|
||||
|
||||
beforeEach(function() {
|
||||
fakeGetWindowData = {
|
||||
windowPath: ""
|
||||
windowPath: "",
|
||||
windowHash: ""
|
||||
};
|
||||
|
||||
sandbox.stub(loop.shared.utils, "getUnsupportedPlatform").returns();
|
||||
@ -177,7 +178,7 @@ describe("loop.store.StandaloneAppStore", function () {
|
||||
});
|
||||
});
|
||||
|
||||
it("should set the loopToken on the conversation for call paths",
|
||||
it("should dispatch a FetchServerData action for call paths",
|
||||
function() {
|
||||
fakeGetWindowData.windowPath = "/c/fakecalltoken";
|
||||
|
||||
@ -187,14 +188,15 @@ describe("loop.store.StandaloneAppStore", function () {
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.FetchServerData({
|
||||
cryptoKey: null,
|
||||
windowType: "outgoing",
|
||||
token: "fakecalltoken"
|
||||
}));
|
||||
});
|
||||
|
||||
it("should set the loopToken on the conversation for room paths",
|
||||
it("should dispatch a FetchServerData action for room paths",
|
||||
function() {
|
||||
fakeGetWindowData.windowPath = "/c/fakeroomtoken";
|
||||
fakeGetWindowData.windowPath = "/fakeroomtoken";
|
||||
|
||||
store.extractTokenInfo(
|
||||
new sharedActions.ExtractTokenInfo(fakeGetWindowData));
|
||||
@ -202,11 +204,29 @@ describe("loop.store.StandaloneAppStore", function () {
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.FetchServerData({
|
||||
windowType: "outgoing",
|
||||
cryptoKey: null,
|
||||
windowType: "room",
|
||||
token: "fakeroomtoken"
|
||||
}));
|
||||
});
|
||||
|
||||
it("should dispatch a FetchServerData action with a crypto key extracted from the hash", function() {
|
||||
fakeGetWindowData = {
|
||||
windowPath: "/fakeroomtoken",
|
||||
windowHash: "#fakeKey"
|
||||
};
|
||||
|
||||
store.extractTokenInfo(
|
||||
new sharedActions.ExtractTokenInfo(fakeGetWindowData));
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.FetchServerData({
|
||||
cryptoKey: "fakeKey",
|
||||
windowType: "room",
|
||||
token: "fakeroomtoken"
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -11,6 +11,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
|
||||
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
|
||||
var sharedActions = loop.shared.actions;
|
||||
|
||||
var sandbox, dispatcher, activeRoomStore, feedbackStore, dispatch;
|
||||
@ -38,6 +39,44 @@ describe("loop.standaloneRoomViews", function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("StandaloneRoomContextView", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(navigator.mozL10n, "get").returnsArg(0);
|
||||
});
|
||||
|
||||
function mountTestComponent(props) {
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(
|
||||
loop.standaloneRoomViews.StandaloneRoomContextView, props));
|
||||
}
|
||||
|
||||
it("should display the room name if no failures are known", function() {
|
||||
var view = mountTestComponent({
|
||||
roomName: "Mike's room"
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().textContent).eql("Mike's room");
|
||||
});
|
||||
|
||||
it("should display an unsupported browser message if crypto is unsupported", function() {
|
||||
var view = mountTestComponent({
|
||||
roomName: "Mark's room",
|
||||
roomInfoFailure: ROOM_INFO_FAILURES.WEB_CRYPTO_UNSUPPORTED
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().textContent).match(/unsupported/);
|
||||
});
|
||||
|
||||
it("should display a general error message for any other failure", function() {
|
||||
var view = mountTestComponent({
|
||||
roomName: "Mark's room",
|
||||
roomInfoFailure: ROOM_INFO_FAILURES.NO_DATA
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().textContent).match(/not_available/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("StandaloneRoomView", function() {
|
||||
function mountTestComponent() {
|
||||
return TestUtils.renderIntoDocument(
|
||||
|
@ -71,10 +71,10 @@ describe("loop.webapp", function() {
|
||||
}));
|
||||
});
|
||||
|
||||
it("should dispatch a ExtractTokenInfo action with the path",
|
||||
it("should dispatch a ExtractTokenInfo action with the path and hash",
|
||||
function() {
|
||||
sandbox.stub(loop.shared.utils, "locationData").returns({
|
||||
hash: "",
|
||||
hash: "#fakeKey",
|
||||
pathname: "/c/faketoken"
|
||||
});
|
||||
|
||||
@ -83,7 +83,8 @@ describe("loop.webapp", function() {
|
||||
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
|
||||
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
|
||||
new sharedActions.ExtractTokenInfo({
|
||||
windowPath: "/c/faketoken"
|
||||
windowPath: "/c/faketoken",
|
||||
windowHash: "#fakeKey"
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user