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;
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.standalone .room-conversation h2.room-name {
|
.standalone .room-conversation h2.room-name,
|
||||||
|
.standalone .room-conversation h2.room-info-failure {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 10px;
|
||||||
|
/* 20px is 10px for left and right margins. */
|
||||||
|
width: calc(25% - 20px);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
z-index: 2000000;
|
z-index: 2000000;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
|
@ -43,7 +43,8 @@ loop.shared.actions = (function() {
|
|||||||
* Extract the token information and type for the standalone window
|
* Extract the token information and type for the standalone window
|
||||||
*/
|
*/
|
||||||
ExtractTokenInfo: Action.define("extractTokenInfo", {
|
ExtractTokenInfo: Action.define("extractTokenInfo", {
|
||||||
windowPath: String
|
windowPath: String,
|
||||||
|
windowHash: String
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,6 +66,7 @@ loop.shared.actions = (function() {
|
|||||||
* token.
|
* token.
|
||||||
*/
|
*/
|
||||||
FetchServerData: Action.define("fetchServerData", {
|
FetchServerData: Action.define("fetchServerData", {
|
||||||
|
// cryptoKey: String - Optional.
|
||||||
token: String,
|
token: String,
|
||||||
windowType: String
|
windowType: String
|
||||||
}),
|
}),
|
||||||
@ -386,6 +388,7 @@ loop.shared.actions = (function() {
|
|||||||
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
|
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
|
||||||
*/
|
*/
|
||||||
UpdateRoomInfo: Action.define("updateRoomInfo", {
|
UpdateRoomInfo: Action.define("updateRoomInfo", {
|
||||||
|
// context: Object - Optional.
|
||||||
// roomName: String - Optional.
|
// roomName: String - Optional.
|
||||||
roomOwner: String,
|
roomOwner: String,
|
||||||
roomUrl: String
|
roomUrl: String
|
||||||
|
@ -11,6 +11,7 @@ loop.store.ActiveRoomStore = (function() {
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var sharedActions = loop.shared.actions;
|
var sharedActions = loop.shared.actions;
|
||||||
|
var crypto = loop.crypto;
|
||||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
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_STATES = loop.store.ROOM_STATES;
|
||||||
|
|
||||||
|
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Active room store.
|
* Active room store.
|
||||||
*
|
*
|
||||||
@ -76,7 +79,11 @@ loop.store.ActiveRoomStore = (function() {
|
|||||||
localVideoDimensions: {},
|
localVideoDimensions: {},
|
||||||
remoteVideoDimensions: {},
|
remoteVideoDimensions: {},
|
||||||
screenSharingState: SCREEN_SHARE_STATES.INACTIVE,
|
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({
|
this.setStoreState({
|
||||||
roomToken: actionData.token,
|
roomToken: actionData.token,
|
||||||
|
roomCryptoKey: actionData.cryptoKey,
|
||||||
roomState: ROOM_STATES.READY
|
roomState: ROOM_STATES.READY
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -207,17 +215,64 @@ loop.store.ActiveRoomStore = (function() {
|
|||||||
this._mozLoop.rooms.on("delete:" + actionData.roomToken,
|
this._mozLoop.rooms.on("delete:" + actionData.roomToken,
|
||||||
this._handleRoomDelete.bind(this));
|
this._handleRoomDelete.bind(this));
|
||||||
|
|
||||||
this._mozLoop.rooms.get(this._storeState.roomToken,
|
this._getRoomDataForStandalone();
|
||||||
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.dispatcher.dispatch(new sharedActions.UpdateRoomInfo(result));
|
_getRoomDataForStandalone: function() {
|
||||||
}.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
updateRoomInfo: function(actionData) {
|
||||||
this.setStoreState({
|
this.setStoreState({
|
||||||
|
roomInfoFailure: actionData.roomInfoFailure,
|
||||||
roomName: actionData.roomName,
|
roomName: actionData.roomName,
|
||||||
roomOwner: actionData.roomOwner,
|
roomOwner: actionData.roomOwner,
|
||||||
roomUrl: actionData.roomUrl
|
roomUrl: actionData.roomUrl
|
||||||
|
@ -43,6 +43,17 @@ loop.shared.utils = (function(mozL10n) {
|
|||||||
UNKNOWN: "reason-unknown"
|
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 = {
|
var STREAM_PROPERTIES = {
|
||||||
VIDEO_DIMENSIONS: "videoDimensions",
|
VIDEO_DIMENSIONS: "videoDimensions",
|
||||||
HAS_AUDIO: "hasAudio",
|
HAS_AUDIO: "hasAudio",
|
||||||
@ -397,6 +408,7 @@ loop.shared.utils = (function(mozL10n) {
|
|||||||
WEBSOCKET_REASONS: WEBSOCKET_REASONS,
|
WEBSOCKET_REASONS: WEBSOCKET_REASONS,
|
||||||
STREAM_PROPERTIES: STREAM_PROPERTIES,
|
STREAM_PROPERTIES: STREAM_PROPERTIES,
|
||||||
SCREEN_SHARE_STATES: SCREEN_SHARE_STATES,
|
SCREEN_SHARE_STATES: SCREEN_SHARE_STATES,
|
||||||
|
ROOM_INFO_FAILURES: ROOM_INFO_FAILURES,
|
||||||
composeCallUrlEmail: composeCallUrlEmail,
|
composeCallUrlEmail: composeCallUrlEmail,
|
||||||
formatDate: formatDate,
|
formatDate: formatDate,
|
||||||
getBoolPreference: getBoolPreference,
|
getBoolPreference: getBoolPreference,
|
||||||
|
@ -108,6 +108,7 @@
|
|||||||
<!-- app scripts -->
|
<!-- app scripts -->
|
||||||
<script type="text/javascript" src="config.js"></script>
|
<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/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/models.js"></script>
|
||||||
<script type="text/javascript" src="shared/js/mixins.js"></script>
|
<script type="text/javascript" src="shared/js/mixins.js"></script>
|
||||||
<script type="text/javascript" src="shared/js/feedbackApiClient.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];
|
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
|
* 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
|
* @param {sharedActions.GetWindowData} actionData The action data
|
||||||
*/
|
*/
|
||||||
@ -135,6 +147,7 @@ loop.store.StandaloneAppStore = (function() {
|
|||||||
// it.
|
// it.
|
||||||
if (token) {
|
if (token) {
|
||||||
this._dispatcher.dispatch(new loop.shared.actions.FetchServerData({
|
this._dispatcher.dispatch(new loop.shared.actions.FetchServerData({
|
||||||
|
cryptoKey: this._extractCryptoKey(actionData.windowHash),
|
||||||
token: token,
|
token: token,
|
||||||
windowType: windowType
|
windowType: windowType
|
||||||
}));
|
}));
|
||||||
|
@ -12,6 +12,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
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 ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
var sharedActions = loop.shared.actions;
|
var sharedActions = loop.shared.actions;
|
||||||
var sharedMixins = loop.shared.mixins;
|
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",
|
var StandaloneRoomView = React.createClass({displayName: "StandaloneRoomView",
|
||||||
mixins: [
|
mixins: [
|
||||||
Backbone.Events,
|
Backbone.Events,
|
||||||
@ -458,7 +482,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
roomUsed: this.state.used}),
|
roomUsed: this.state.used}),
|
||||||
React.createElement("div", {className: "video-layout-wrapper"},
|
React.createElement("div", {className: "video-layout-wrapper"},
|
||||||
React.createElement("div", {className: "conversation room-conversation"},
|
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("div", {className: "media nested"},
|
||||||
React.createElement("span", {className: "self-view-hidden-message"},
|
React.createElement("span", {className: "self-view-hidden-message"},
|
||||||
mozL10n.get("self_view_hidden_message")
|
mozL10n.get("self_view_hidden_message")
|
||||||
@ -491,6 +516,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
StandaloneRoomContextView: StandaloneRoomContextView,
|
||||||
StandaloneRoomView: StandaloneRoomView
|
StandaloneRoomView: StandaloneRoomView
|
||||||
};
|
};
|
||||||
})(navigator.mozL10n);
|
})(navigator.mozL10n);
|
||||||
|
@ -12,6 +12,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
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 ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
var sharedActions = loop.shared.actions;
|
var sharedActions = loop.shared.actions;
|
||||||
var sharedMixins = loop.shared.mixins;
|
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({
|
var StandaloneRoomView = React.createClass({
|
||||||
mixins: [
|
mixins: [
|
||||||
Backbone.Events,
|
Backbone.Events,
|
||||||
@ -458,7 +482,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
roomUsed={this.state.used} />
|
roomUsed={this.state.used} />
|
||||||
<div className="video-layout-wrapper">
|
<div className="video-layout-wrapper">
|
||||||
<div className="conversation room-conversation">
|
<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">
|
<div className="media nested">
|
||||||
<span className="self-view-hidden-message">
|
<span className="self-view-hidden-message">
|
||||||
{mozL10n.get("self_view_hidden_message")}
|
{mozL10n.get("self_view_hidden_message")}
|
||||||
@ -491,6 +516,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
StandaloneRoomContextView: StandaloneRoomContextView,
|
||||||
StandaloneRoomView: StandaloneRoomView
|
StandaloneRoomView: StandaloneRoomView
|
||||||
};
|
};
|
||||||
})(navigator.mozL10n);
|
})(navigator.mozL10n);
|
||||||
|
@ -1109,7 +1109,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
var locationData = sharedUtils.locationData();
|
var locationData = sharedUtils.locationData();
|
||||||
|
|
||||||
dispatcher.dispatch(new sharedActions.ExtractTokenInfo({
|
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();
|
var locationData = sharedUtils.locationData();
|
||||||
|
|
||||||
dispatcher.dispatch(new sharedActions.ExtractTokenInfo({
|
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_display_name_guest=Guest
|
||||||
rooms_unavailable_notification_message=Sorry, you cannot join this conversation. The link may be expired or invalid.
|
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.
|
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
|
## LOCALIZATION_NOTE(standalone_title_with_status): {{clientShortname}} will be
|
||||||
## replaced by the brand name and {{currentStatus}} will be replaced
|
## 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 ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
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 sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver;
|
||||||
var fakeMultiplexGum;
|
var fakeMultiplexGum;
|
||||||
|
|
||||||
@ -347,20 +348,127 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||||||
sinon.assert.calledOnce(fakeMozLoop.rooms.get);
|
sinon.assert.calledOnce(fakeMozLoop.rooms.get);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should dispatch UpdateRoomInfo if mozLoop.rooms.get is successful", function() {
|
it("should dispatch an UpdateRoomInfo message with 'no data' failure if neither roomName nor context are supplied", function() {
|
||||||
var roomDetails = {
|
fakeMozLoop.rooms.get.callsArgWith(1, null, {
|
||||||
roomName: "fakeName",
|
roomOwner: "Dan",
|
||||||
roomUrl: "http://invalid",
|
roomUrl: "http://invalid"
|
||||||
roomOwner: "gavin"
|
});
|
||||||
};
|
|
||||||
|
|
||||||
fakeMozLoop.rooms.get.callsArgWith(1, null, roomDetails);
|
|
||||||
|
|
||||||
store.fetchServerData(fetchServerAction);
|
store.fetchServerData(fetchServerAction);
|
||||||
|
|
||||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
sinon.assert.calledWithExactly(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() {
|
beforeEach(function() {
|
||||||
fakeGetWindowData = {
|
fakeGetWindowData = {
|
||||||
windowPath: ""
|
windowPath: "",
|
||||||
|
windowHash: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
sandbox.stub(loop.shared.utils, "getUnsupportedPlatform").returns();
|
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() {
|
function() {
|
||||||
fakeGetWindowData.windowPath = "/c/fakecalltoken";
|
fakeGetWindowData.windowPath = "/c/fakecalltoken";
|
||||||
|
|
||||||
@ -187,14 +188,15 @@ describe("loop.store.StandaloneAppStore", function () {
|
|||||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||||
new sharedActions.FetchServerData({
|
new sharedActions.FetchServerData({
|
||||||
|
cryptoKey: null,
|
||||||
windowType: "outgoing",
|
windowType: "outgoing",
|
||||||
token: "fakecalltoken"
|
token: "fakecalltoken"
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set the loopToken on the conversation for room paths",
|
it("should dispatch a FetchServerData action for room paths",
|
||||||
function() {
|
function() {
|
||||||
fakeGetWindowData.windowPath = "/c/fakeroomtoken";
|
fakeGetWindowData.windowPath = "/fakeroomtoken";
|
||||||
|
|
||||||
store.extractTokenInfo(
|
store.extractTokenInfo(
|
||||||
new sharedActions.ExtractTokenInfo(fakeGetWindowData));
|
new sharedActions.ExtractTokenInfo(fakeGetWindowData));
|
||||||
@ -202,11 +204,29 @@ describe("loop.store.StandaloneAppStore", function () {
|
|||||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||||
new sharedActions.FetchServerData({
|
new sharedActions.FetchServerData({
|
||||||
windowType: "outgoing",
|
cryptoKey: null,
|
||||||
|
windowType: "room",
|
||||||
token: "fakeroomtoken"
|
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 ROOM_STATES = loop.store.ROOM_STATES;
|
||||||
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
|
var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
|
||||||
|
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
|
||||||
var sharedActions = loop.shared.actions;
|
var sharedActions = loop.shared.actions;
|
||||||
|
|
||||||
var sandbox, dispatcher, activeRoomStore, feedbackStore, dispatch;
|
var sandbox, dispatcher, activeRoomStore, feedbackStore, dispatch;
|
||||||
@ -38,6 +39,44 @@ describe("loop.standaloneRoomViews", function() {
|
|||||||
sandbox.restore();
|
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() {
|
describe("StandaloneRoomView", function() {
|
||||||
function mountTestComponent() {
|
function mountTestComponent() {
|
||||||
return TestUtils.renderIntoDocument(
|
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() {
|
function() {
|
||||||
sandbox.stub(loop.shared.utils, "locationData").returns({
|
sandbox.stub(loop.shared.utils, "locationData").returns({
|
||||||
hash: "",
|
hash: "#fakeKey",
|
||||||
pathname: "/c/faketoken"
|
pathname: "/c/faketoken"
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -83,7 +83,8 @@ describe("loop.webapp", function() {
|
|||||||
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
|
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
|
||||||
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
|
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
|
||||||
new sharedActions.ExtractTokenInfo({
|
new sharedActions.ExtractTokenInfo({
|
||||||
windowPath: "/c/faketoken"
|
windowPath: "/c/faketoken",
|
||||||
|
windowHash: "#fakeKey"
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user