mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1088672 - Part 7. Rewrite Loop's incoming call handling in the flux style. Remove the now redundant non-flux based code for incoming calls. r=mikedeboer
This commit is contained in:
parent
fcf1584a61
commit
93876de832
@ -26,7 +26,6 @@
|
||||
<script type="text/javascript" src="loop/shared/libs/backbone-1.1.2.js"></script>
|
||||
|
||||
<script type="text/javascript" src="loop/shared/js/utils.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/models.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/mixins.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/feedbackApiClient.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/actions.js"></script>
|
||||
|
@ -75,37 +75,6 @@ loop.Client = (function($) {
|
||||
cb(error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Block call URL based on the token identifier
|
||||
*
|
||||
* @param {string} token Conversation identifier used to block the URL
|
||||
* @param {mozLoop.LOOP_SESSION_TYPE} sessionType The type of session which
|
||||
* the url belongs to.
|
||||
* @param {function} cb Callback function used for handling an error
|
||||
* response. XXX The incoming call panel does not
|
||||
* exist after the block button is clicked therefore
|
||||
* it does not make sense to display an error.
|
||||
**/
|
||||
deleteCallUrl: function(token, sessionType, cb) {
|
||||
function deleteRequestCallback(error, responseText) {
|
||||
if (error) {
|
||||
this._failureHandler(cb, error);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
cb(null);
|
||||
} catch (err) {
|
||||
console.log("Error deleting call info", err);
|
||||
cb(err);
|
||||
}
|
||||
}
|
||||
|
||||
this.mozLoop.hawkRequest(sessionType,
|
||||
"/call-url/" + token, "DELETE", null,
|
||||
deleteRequestCallback.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up an outgoing call, getting the relevant data from the server.
|
||||
*
|
||||
|
@ -16,7 +16,6 @@ loop.conversation = (function(mozL10n) {
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedActions = loop.shared.actions;
|
||||
|
||||
var IncomingConversationView = loop.conversationViews.IncomingConversationView;
|
||||
var CallControllerView = loop.conversationViews.CallControllerView;
|
||||
var CallIdentifierView = loop.conversationViews.CallIdentifierView;
|
||||
var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
|
||||
|
@ -16,7 +16,6 @@ loop.conversation = (function(mozL10n) {
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedActions = loop.shared.actions;
|
||||
|
||||
var IncomingConversationView = loop.conversationViews.IncomingConversationView;
|
||||
var CallControllerView = loop.conversationViews.CallControllerView;
|
||||
var CallIdentifierView = loop.conversationViews.CallIdentifierView;
|
||||
var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
|
||||
|
@ -69,12 +69,7 @@ loop.store.ConversationAppStore = (function() {
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX windowData is a hack for the IncomingConversationView until
|
||||
// we rework it for the flux model in bug 1088672.
|
||||
this.setStoreState({
|
||||
windowType: windowData.type,
|
||||
windowData: windowData
|
||||
});
|
||||
this.setStoreState({windowType: windowData.type});
|
||||
|
||||
this._dispatcher.dispatch(new loop.shared.actions.SetupWindowData(_.extend({
|
||||
windowId: actionData.windowId}, windowData)));
|
||||
|
@ -314,9 +314,6 @@ loop.conversationViews = (function(mozL10n) {
|
||||
|
||||
/**
|
||||
* Something went wrong view. Displayed when there's a big problem.
|
||||
*
|
||||
* XXX Based on CallFailedView, but built specially until we flux-ify the
|
||||
* incoming call views (bug 1088672).
|
||||
*/
|
||||
var GenericFailureView = React.createClass({displayName: "GenericFailureView",
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
@ -347,327 +344,6 @@ loop.conversationViews = (function(mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This view manages the incoming conversation views - from
|
||||
* call initiation through to the actual conversation and call end.
|
||||
*
|
||||
* At the moment, it does more than that, these parts need refactoring out.
|
||||
*/
|
||||
var IncomingConversationView = React.createClass({displayName: "IncomingConversationView",
|
||||
mixins: [sharedMixins.AudioMixin, sharedMixins.WindowCloseMixin],
|
||||
|
||||
propTypes: {
|
||||
client: React.PropTypes.instanceOf(loop.Client).isRequired,
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
isDesktop: React.PropTypes.bool,
|
||||
conversationAppStore: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationAppStore).isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
isDesktop: false
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
callFailed: false, // XXX this should be removed when bug 1047410 lands.
|
||||
callStatus: "start"
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.props.conversation.on("accept", this.accept, this);
|
||||
this.props.conversation.on("decline", this.decline, this);
|
||||
this.props.conversation.on("declineAndBlock", this.declineAndBlock, this);
|
||||
this.props.conversation.on("call:accepted", this.accepted, this);
|
||||
this.props.conversation.on("change:publishedStream", this._checkConnected, this);
|
||||
this.props.conversation.on("change:subscribedStream", this._checkConnected, this);
|
||||
this.props.conversation.on("session:ended", this.endCall, this);
|
||||
this.props.conversation.on("session:peer-hungup", this._onPeerHungup, this);
|
||||
this.props.conversation.on("session:network-disconnected", this._onNetworkDisconnected, this);
|
||||
this.props.conversation.on("session:connection-error", this._notifyError, this);
|
||||
|
||||
this.setupIncomingCall();
|
||||
},
|
||||
|
||||
componentDidUnmount: function() {
|
||||
this.props.conversation.off(null, null, this);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
switch (this.state.callStatus) {
|
||||
case "start": {
|
||||
document.title = mozL10n.get("incoming_call_title2");
|
||||
|
||||
// XXX Don't render anything initially, though this should probably
|
||||
// be some sort of pending view, whilst we connect the websocket.
|
||||
return null;
|
||||
}
|
||||
case "incoming": {
|
||||
document.title = mozL10n.get("incoming_call_title2");
|
||||
|
||||
return (
|
||||
React.createElement(AcceptCallView, {
|
||||
model: this.props.conversation,
|
||||
video: this.props.conversation.hasVideoStream("incoming")}
|
||||
)
|
||||
);
|
||||
}
|
||||
case "connected": {
|
||||
document.title = this.props.conversation.getCallIdentifier();
|
||||
|
||||
var callType = this.props.conversation.get("selectedCallType");
|
||||
|
||||
return (
|
||||
React.createElement(sharedViews.ConversationView, {
|
||||
isDesktop: this.props.isDesktop,
|
||||
initiate: true,
|
||||
sdk: this.props.sdk,
|
||||
model: this.props.conversation,
|
||||
video: {enabled: callType !== "audio"}}
|
||||
)
|
||||
);
|
||||
}
|
||||
case "end": {
|
||||
// XXX To be handled with the "failed" view state when bug 1047410 lands
|
||||
if (this.state.callFailed) {
|
||||
return React.createElement(GenericFailureView, {
|
||||
cancelCall: this.closeWindow.bind(this)}
|
||||
);
|
||||
}
|
||||
|
||||
document.title = mozL10n.get("conversation_has_ended");
|
||||
|
||||
this.play("terminated");
|
||||
|
||||
return (
|
||||
React.createElement(sharedViews.FeedbackView, {
|
||||
onAfterFeedbackReceived: this.closeWindow.bind(this)}
|
||||
)
|
||||
);
|
||||
}
|
||||
case "close": {
|
||||
this.closeWindow();
|
||||
return (React.createElement("div", null));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify the user that the connection was not possible
|
||||
* @param {{code: number, message: string}} error
|
||||
*/
|
||||
_notifyError: function(error) {
|
||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||
// this by better "call failed" UI.
|
||||
console.error(error);
|
||||
this.setState({callFailed: true, callStatus: "end"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Peer hung up. Notifies the user and ends the call.
|
||||
*
|
||||
* Event properties:
|
||||
* - {String} connectionId: OT session id
|
||||
*/
|
||||
_onPeerHungup: function() {
|
||||
this.setState({callFailed: false, callStatus: "end"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Network disconnected. Notifies the user and ends the call.
|
||||
*/
|
||||
_onNetworkDisconnected: function() {
|
||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||
// this by better "call failed" UI.
|
||||
this.setState({callFailed: true, callStatus: "end"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Incoming call route.
|
||||
*/
|
||||
setupIncomingCall: function() {
|
||||
navigator.mozLoop.startAlerting();
|
||||
|
||||
// XXX This is a hack until we rework for the flux model in bug 1088672.
|
||||
var callData = this.props.conversationAppStore.getStoreState().windowData;
|
||||
|
||||
this.props.conversation.setIncomingSessionData(callData);
|
||||
this._setupWebSocket();
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the actual conversation
|
||||
*/
|
||||
accepted: function() {
|
||||
this.setState({callStatus: "connected"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Moves the call to the end state
|
||||
*/
|
||||
endCall: function() {
|
||||
navigator.mozLoop.calls.clearCallInProgress(
|
||||
this.props.conversation.get("windowId"));
|
||||
this.setState({callStatus: "end"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to set up the web socket connection and navigate to the
|
||||
* call view if appropriate.
|
||||
*/
|
||||
_setupWebSocket: function() {
|
||||
this._websocket = new loop.CallConnectionWebSocket({
|
||||
url: this.props.conversation.get("progressURL"),
|
||||
websocketToken: this.props.conversation.get("websocketToken"),
|
||||
callId: this.props.conversation.get("callId"),
|
||||
});
|
||||
this._websocket.promiseConnect().then(function(progressStatus) {
|
||||
this.setState({
|
||||
callStatus: progressStatus === "terminated" ? "close" : "incoming"
|
||||
});
|
||||
}.bind(this), function() {
|
||||
this._handleSessionError();
|
||||
return;
|
||||
}.bind(this));
|
||||
|
||||
this._websocket.on("progress", this._handleWebSocketProgress, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the streams have been connected, and notifies the
|
||||
* websocket that the media is now connected.
|
||||
*/
|
||||
_checkConnected: function() {
|
||||
// Check we've had both local and remote streams connected before
|
||||
// sending the media up message.
|
||||
if (this.props.conversation.streamsConnected()) {
|
||||
this._websocket.mediaUp();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to receive websocket progress and to determine how to handle
|
||||
* it if appropraite.
|
||||
* If we add more cases here, then we should refactor this function.
|
||||
*
|
||||
* @param {Object} progressData The progress data from the websocket.
|
||||
* @param {String} previousState The previous state from the websocket.
|
||||
*/
|
||||
_handleWebSocketProgress: function(progressData, previousState) {
|
||||
// We only care about the terminated state at the moment.
|
||||
if (progressData.state !== "terminated")
|
||||
return;
|
||||
|
||||
// XXX This would be nicer in the _abortIncomingCall function, but we need to stop
|
||||
// it here for now due to server-side issues that are being fixed in bug 1088351.
|
||||
// This is before the abort call to ensure that it happens before the window is
|
||||
// closed.
|
||||
navigator.mozLoop.stopAlerting();
|
||||
|
||||
// If we hit any of the termination reasons, and the user hasn't accepted
|
||||
// then it seems reasonable to close the window/abort the incoming call.
|
||||
//
|
||||
// If the user has accepted the call, and something's happened, display
|
||||
// the call failed view.
|
||||
//
|
||||
// https://wiki.mozilla.org/Loop/Architecture/MVP#Termination_Reasons
|
||||
if (previousState === "init" || previousState === "alerting") {
|
||||
this._abortIncomingCall();
|
||||
} else {
|
||||
this.setState({callFailed: true, callStatus: "end"});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Silently aborts an incoming call - stops the alerting, and
|
||||
* closes the websocket.
|
||||
*/
|
||||
_abortIncomingCall: function() {
|
||||
this._websocket.close();
|
||||
// Having a timeout here lets the logging for the websocket complete and be
|
||||
// displayed on the console if both are on.
|
||||
setTimeout(this.closeWindow, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Accepts an incoming call.
|
||||
*/
|
||||
accept: function() {
|
||||
navigator.mozLoop.stopAlerting();
|
||||
this._websocket.accept();
|
||||
this.props.conversation.accepted();
|
||||
},
|
||||
|
||||
/**
|
||||
* Declines a call and handles closing of the window.
|
||||
*/
|
||||
_declineCall: function() {
|
||||
this._websocket.decline();
|
||||
navigator.mozLoop.calls.clearCallInProgress(
|
||||
this.props.conversation.get("windowId"));
|
||||
this._websocket.close();
|
||||
// Having a timeout here lets the logging for the websocket complete and be
|
||||
// displayed on the console if both are on.
|
||||
setTimeout(this.closeWindow, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Declines an incoming call.
|
||||
*/
|
||||
decline: function() {
|
||||
navigator.mozLoop.stopAlerting();
|
||||
this._declineCall();
|
||||
},
|
||||
|
||||
/**
|
||||
* Decline and block an incoming call
|
||||
* @note:
|
||||
* - loopToken is the callUrl identifier. It gets set in the panel
|
||||
* after a callUrl is received
|
||||
*/
|
||||
declineAndBlock: function() {
|
||||
navigator.mozLoop.stopAlerting();
|
||||
var token = this.props.conversation.get("callToken");
|
||||
var callerId = this.props.conversation.get("callerId");
|
||||
|
||||
// If this is a direct call, we'll need to block the caller directly.
|
||||
if (callerId && EMAIL_OR_PHONE_RE.test(callerId)) {
|
||||
navigator.mozLoop.calls.blockDirectCaller(callerId, function(err) {
|
||||
// XXX The conversation window will be closed when this cb is triggered
|
||||
// figure out if there is a better way to report the error to the user
|
||||
// (bug 1103150).
|
||||
console.log(err.fileName + ":" + err.lineNumber + ": " + err.message);
|
||||
});
|
||||
} else {
|
||||
this.props.client.deleteCallUrl(token,
|
||||
this.props.conversation.get("sessionType"),
|
||||
function(error) {
|
||||
// XXX The conversation window will be closed when this cb is triggered
|
||||
// figure out if there is a better way to report the error to the user
|
||||
// (bug 1048909).
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
this._declineCall();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles a error starting the session
|
||||
*/
|
||||
_handleSessionError: function() {
|
||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||
// this by better "call failed" UI.
|
||||
console.error("Failed initiating the call session.");
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* View for pending conversations. Displays a cancel button and appropriate
|
||||
* pending/ringing strings.
|
||||
@ -1083,7 +759,6 @@ loop.conversationViews = (function(mozL10n) {
|
||||
_getContactDisplayName: _getContactDisplayName,
|
||||
GenericFailureView: GenericFailureView,
|
||||
AcceptCallView: AcceptCallView,
|
||||
IncomingConversationView: IncomingConversationView,
|
||||
OngoingConversationView: OngoingConversationView,
|
||||
CallControllerView: CallControllerView
|
||||
};
|
||||
|
@ -314,9 +314,6 @@ loop.conversationViews = (function(mozL10n) {
|
||||
|
||||
/**
|
||||
* Something went wrong view. Displayed when there's a big problem.
|
||||
*
|
||||
* XXX Based on CallFailedView, but built specially until we flux-ify the
|
||||
* incoming call views (bug 1088672).
|
||||
*/
|
||||
var GenericFailureView = React.createClass({
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
@ -347,327 +344,6 @@ loop.conversationViews = (function(mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This view manages the incoming conversation views - from
|
||||
* call initiation through to the actual conversation and call end.
|
||||
*
|
||||
* At the moment, it does more than that, these parts need refactoring out.
|
||||
*/
|
||||
var IncomingConversationView = React.createClass({
|
||||
mixins: [sharedMixins.AudioMixin, sharedMixins.WindowCloseMixin],
|
||||
|
||||
propTypes: {
|
||||
client: React.PropTypes.instanceOf(loop.Client).isRequired,
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
isDesktop: React.PropTypes.bool,
|
||||
conversationAppStore: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationAppStore).isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
isDesktop: false
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
callFailed: false, // XXX this should be removed when bug 1047410 lands.
|
||||
callStatus: "start"
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.props.conversation.on("accept", this.accept, this);
|
||||
this.props.conversation.on("decline", this.decline, this);
|
||||
this.props.conversation.on("declineAndBlock", this.declineAndBlock, this);
|
||||
this.props.conversation.on("call:accepted", this.accepted, this);
|
||||
this.props.conversation.on("change:publishedStream", this._checkConnected, this);
|
||||
this.props.conversation.on("change:subscribedStream", this._checkConnected, this);
|
||||
this.props.conversation.on("session:ended", this.endCall, this);
|
||||
this.props.conversation.on("session:peer-hungup", this._onPeerHungup, this);
|
||||
this.props.conversation.on("session:network-disconnected", this._onNetworkDisconnected, this);
|
||||
this.props.conversation.on("session:connection-error", this._notifyError, this);
|
||||
|
||||
this.setupIncomingCall();
|
||||
},
|
||||
|
||||
componentDidUnmount: function() {
|
||||
this.props.conversation.off(null, null, this);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
switch (this.state.callStatus) {
|
||||
case "start": {
|
||||
document.title = mozL10n.get("incoming_call_title2");
|
||||
|
||||
// XXX Don't render anything initially, though this should probably
|
||||
// be some sort of pending view, whilst we connect the websocket.
|
||||
return null;
|
||||
}
|
||||
case "incoming": {
|
||||
document.title = mozL10n.get("incoming_call_title2");
|
||||
|
||||
return (
|
||||
<AcceptCallView
|
||||
model={this.props.conversation}
|
||||
video={this.props.conversation.hasVideoStream("incoming")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "connected": {
|
||||
document.title = this.props.conversation.getCallIdentifier();
|
||||
|
||||
var callType = this.props.conversation.get("selectedCallType");
|
||||
|
||||
return (
|
||||
<sharedViews.ConversationView
|
||||
isDesktop={this.props.isDesktop}
|
||||
initiate={true}
|
||||
sdk={this.props.sdk}
|
||||
model={this.props.conversation}
|
||||
video={{enabled: callType !== "audio"}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "end": {
|
||||
// XXX To be handled with the "failed" view state when bug 1047410 lands
|
||||
if (this.state.callFailed) {
|
||||
return <GenericFailureView
|
||||
cancelCall={this.closeWindow.bind(this)}
|
||||
/>;
|
||||
}
|
||||
|
||||
document.title = mozL10n.get("conversation_has_ended");
|
||||
|
||||
this.play("terminated");
|
||||
|
||||
return (
|
||||
<sharedViews.FeedbackView
|
||||
onAfterFeedbackReceived={this.closeWindow.bind(this)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "close": {
|
||||
this.closeWindow();
|
||||
return (<div/>);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify the user that the connection was not possible
|
||||
* @param {{code: number, message: string}} error
|
||||
*/
|
||||
_notifyError: function(error) {
|
||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||
// this by better "call failed" UI.
|
||||
console.error(error);
|
||||
this.setState({callFailed: true, callStatus: "end"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Peer hung up. Notifies the user and ends the call.
|
||||
*
|
||||
* Event properties:
|
||||
* - {String} connectionId: OT session id
|
||||
*/
|
||||
_onPeerHungup: function() {
|
||||
this.setState({callFailed: false, callStatus: "end"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Network disconnected. Notifies the user and ends the call.
|
||||
*/
|
||||
_onNetworkDisconnected: function() {
|
||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||
// this by better "call failed" UI.
|
||||
this.setState({callFailed: true, callStatus: "end"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Incoming call route.
|
||||
*/
|
||||
setupIncomingCall: function() {
|
||||
navigator.mozLoop.startAlerting();
|
||||
|
||||
// XXX This is a hack until we rework for the flux model in bug 1088672.
|
||||
var callData = this.props.conversationAppStore.getStoreState().windowData;
|
||||
|
||||
this.props.conversation.setIncomingSessionData(callData);
|
||||
this._setupWebSocket();
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the actual conversation
|
||||
*/
|
||||
accepted: function() {
|
||||
this.setState({callStatus: "connected"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Moves the call to the end state
|
||||
*/
|
||||
endCall: function() {
|
||||
navigator.mozLoop.calls.clearCallInProgress(
|
||||
this.props.conversation.get("windowId"));
|
||||
this.setState({callStatus: "end"});
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to set up the web socket connection and navigate to the
|
||||
* call view if appropriate.
|
||||
*/
|
||||
_setupWebSocket: function() {
|
||||
this._websocket = new loop.CallConnectionWebSocket({
|
||||
url: this.props.conversation.get("progressURL"),
|
||||
websocketToken: this.props.conversation.get("websocketToken"),
|
||||
callId: this.props.conversation.get("callId"),
|
||||
});
|
||||
this._websocket.promiseConnect().then(function(progressStatus) {
|
||||
this.setState({
|
||||
callStatus: progressStatus === "terminated" ? "close" : "incoming"
|
||||
});
|
||||
}.bind(this), function() {
|
||||
this._handleSessionError();
|
||||
return;
|
||||
}.bind(this));
|
||||
|
||||
this._websocket.on("progress", this._handleWebSocketProgress, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the streams have been connected, and notifies the
|
||||
* websocket that the media is now connected.
|
||||
*/
|
||||
_checkConnected: function() {
|
||||
// Check we've had both local and remote streams connected before
|
||||
// sending the media up message.
|
||||
if (this.props.conversation.streamsConnected()) {
|
||||
this._websocket.mediaUp();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to receive websocket progress and to determine how to handle
|
||||
* it if appropraite.
|
||||
* If we add more cases here, then we should refactor this function.
|
||||
*
|
||||
* @param {Object} progressData The progress data from the websocket.
|
||||
* @param {String} previousState The previous state from the websocket.
|
||||
*/
|
||||
_handleWebSocketProgress: function(progressData, previousState) {
|
||||
// We only care about the terminated state at the moment.
|
||||
if (progressData.state !== "terminated")
|
||||
return;
|
||||
|
||||
// XXX This would be nicer in the _abortIncomingCall function, but we need to stop
|
||||
// it here for now due to server-side issues that are being fixed in bug 1088351.
|
||||
// This is before the abort call to ensure that it happens before the window is
|
||||
// closed.
|
||||
navigator.mozLoop.stopAlerting();
|
||||
|
||||
// If we hit any of the termination reasons, and the user hasn't accepted
|
||||
// then it seems reasonable to close the window/abort the incoming call.
|
||||
//
|
||||
// If the user has accepted the call, and something's happened, display
|
||||
// the call failed view.
|
||||
//
|
||||
// https://wiki.mozilla.org/Loop/Architecture/MVP#Termination_Reasons
|
||||
if (previousState === "init" || previousState === "alerting") {
|
||||
this._abortIncomingCall();
|
||||
} else {
|
||||
this.setState({callFailed: true, callStatus: "end"});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Silently aborts an incoming call - stops the alerting, and
|
||||
* closes the websocket.
|
||||
*/
|
||||
_abortIncomingCall: function() {
|
||||
this._websocket.close();
|
||||
// Having a timeout here lets the logging for the websocket complete and be
|
||||
// displayed on the console if both are on.
|
||||
setTimeout(this.closeWindow, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Accepts an incoming call.
|
||||
*/
|
||||
accept: function() {
|
||||
navigator.mozLoop.stopAlerting();
|
||||
this._websocket.accept();
|
||||
this.props.conversation.accepted();
|
||||
},
|
||||
|
||||
/**
|
||||
* Declines a call and handles closing of the window.
|
||||
*/
|
||||
_declineCall: function() {
|
||||
this._websocket.decline();
|
||||
navigator.mozLoop.calls.clearCallInProgress(
|
||||
this.props.conversation.get("windowId"));
|
||||
this._websocket.close();
|
||||
// Having a timeout here lets the logging for the websocket complete and be
|
||||
// displayed on the console if both are on.
|
||||
setTimeout(this.closeWindow, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Declines an incoming call.
|
||||
*/
|
||||
decline: function() {
|
||||
navigator.mozLoop.stopAlerting();
|
||||
this._declineCall();
|
||||
},
|
||||
|
||||
/**
|
||||
* Decline and block an incoming call
|
||||
* @note:
|
||||
* - loopToken is the callUrl identifier. It gets set in the panel
|
||||
* after a callUrl is received
|
||||
*/
|
||||
declineAndBlock: function() {
|
||||
navigator.mozLoop.stopAlerting();
|
||||
var token = this.props.conversation.get("callToken");
|
||||
var callerId = this.props.conversation.get("callerId");
|
||||
|
||||
// If this is a direct call, we'll need to block the caller directly.
|
||||
if (callerId && EMAIL_OR_PHONE_RE.test(callerId)) {
|
||||
navigator.mozLoop.calls.blockDirectCaller(callerId, function(err) {
|
||||
// XXX The conversation window will be closed when this cb is triggered
|
||||
// figure out if there is a better way to report the error to the user
|
||||
// (bug 1103150).
|
||||
console.log(err.fileName + ":" + err.lineNumber + ": " + err.message);
|
||||
});
|
||||
} else {
|
||||
this.props.client.deleteCallUrl(token,
|
||||
this.props.conversation.get("sessionType"),
|
||||
function(error) {
|
||||
// XXX The conversation window will be closed when this cb is triggered
|
||||
// figure out if there is a better way to report the error to the user
|
||||
// (bug 1048909).
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
this._declineCall();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles a error starting the session
|
||||
*/
|
||||
_handleSessionError: function() {
|
||||
// XXX Not the ideal response, but bug 1047410 will be replacing
|
||||
// this by better "call failed" UI.
|
||||
console.error("Failed initiating the call session.");
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* View for pending conversations. Displays a cancel button and appropriate
|
||||
* pending/ringing strings.
|
||||
@ -1083,7 +759,6 @@ loop.conversationViews = (function(mozL10n) {
|
||||
_getContactDisplayName: _getContactDisplayName,
|
||||
GenericFailureView: GenericFailureView,
|
||||
AcceptCallView: AcceptCallView,
|
||||
IncomingConversationView: IncomingConversationView,
|
||||
OngoingConversationView: OngoingConversationView,
|
||||
CallControllerView: CallControllerView
|
||||
};
|
||||
|
@ -52,42 +52,6 @@ describe("loop.Client", function() {
|
||||
});
|
||||
|
||||
describe("loop.Client", function() {
|
||||
describe("#deleteCallUrl", function() {
|
||||
it("should make a delete call to /call-url/{fakeToken}", function() {
|
||||
client.deleteCallUrl(fakeToken, mozLoop.LOOP_SESSION_TYPE.GUEST, callback);
|
||||
|
||||
sinon.assert.calledOnce(hawkRequestStub);
|
||||
sinon.assert.calledWith(hawkRequestStub,
|
||||
mozLoop.LOOP_SESSION_TYPE.GUEST,
|
||||
"/call-url/" + fakeToken, "DELETE");
|
||||
});
|
||||
|
||||
it("should call the callback with null when the request succeeds",
|
||||
function() {
|
||||
|
||||
// Sets up the hawkRequest stub to trigger the callback with no error
|
||||
// and the url.
|
||||
hawkRequestStub.callsArgWith(4, null);
|
||||
|
||||
client.deleteCallUrl(fakeToken, mozLoop.LOOP_SESSION_TYPE.FXA, callback);
|
||||
|
||||
sinon.assert.calledWithExactly(callback, null);
|
||||
});
|
||||
|
||||
it("should send an error when the request fails", function() {
|
||||
// Sets up the hawkRequest stub to trigger the callback with
|
||||
// an error
|
||||
hawkRequestStub.callsArgWith(4, fakeErrorRes);
|
||||
|
||||
client.deleteCallUrl(fakeToken, mozLoop.LOOP_SESSION_TYPE.FXA, callback);
|
||||
|
||||
sinon.assert.calledOnce(callback);
|
||||
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
|
||||
return err.code == 400 && "invalid token" == err.message;
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("#setupOutgoingCall", function() {
|
||||
var calleeIds, callType;
|
||||
|
||||
|
@ -63,8 +63,7 @@ describe("loop.store.ConversationAppStore", function () {
|
||||
dispatcher.dispatch(new sharedActions.GetWindowData(fakeGetWindowData));
|
||||
|
||||
expect(store.getStoreState()).eql({
|
||||
windowType: "incoming",
|
||||
windowData: fakeWindowData
|
||||
windowType: "incoming"
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -718,528 +718,6 @@ describe("loop.conversationViews", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("IncomingConversationView", function() {
|
||||
var conversationAppStore, conversation, client, icView, oldTitle,
|
||||
feedbackStore;
|
||||
|
||||
function mountTestComponent() {
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.conversationViews.IncomingConversationView, {
|
||||
client: client,
|
||||
conversation: conversation,
|
||||
sdk: {},
|
||||
conversationAppStore: conversationAppStore
|
||||
}));
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
oldTitle = document.title;
|
||||
client = new loop.Client();
|
||||
conversation = new loop.shared.models.ConversationModel({}, {
|
||||
sdk: {}
|
||||
});
|
||||
conversation.set({windowId: 42});
|
||||
var dispatcher = new loop.Dispatcher();
|
||||
conversationAppStore = new loop.store.ConversationAppStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
feedbackStore = new loop.store.FeedbackStore(dispatcher, {
|
||||
feedbackClient: {}
|
||||
});
|
||||
sandbox.stub(conversation, "setOutgoingSessionData");
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
icView = undefined;
|
||||
document.title = oldTitle;
|
||||
});
|
||||
|
||||
describe("start", function() {
|
||||
it("should set the title to incoming_call_title2", function() {
|
||||
conversationAppStore.setStoreState({
|
||||
windowData: {
|
||||
progressURL: "fake",
|
||||
websocketToken: "fake",
|
||||
callId: 42
|
||||
}
|
||||
});
|
||||
|
||||
icView = mountTestComponent();
|
||||
|
||||
expect(document.title).eql("incoming_call_title2");
|
||||
});
|
||||
});
|
||||
|
||||
describe("componentDidMount", function() {
|
||||
var fakeSessionData, promise, resolveWebSocketConnect;
|
||||
var rejectWebSocketConnect;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeSessionData = {
|
||||
sessionId: "sessionId",
|
||||
sessionToken: "sessionToken",
|
||||
apiKey: "apiKey",
|
||||
callType: "callType",
|
||||
callId: "Hello",
|
||||
progressURL: "http://progress.example.com",
|
||||
websocketToken: "7b"
|
||||
};
|
||||
|
||||
conversationAppStore.setStoreState({
|
||||
windowData: fakeSessionData
|
||||
});
|
||||
|
||||
stubComponent(loop.conversationViews, "AcceptCallView");
|
||||
stubComponent(sharedView, "ConversationView");
|
||||
});
|
||||
|
||||
it("should start alerting", function() {
|
||||
icView = mountTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.startAlerting);
|
||||
});
|
||||
|
||||
describe("Session Data setup", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(loop, "CallConnectionWebSocket").returns({
|
||||
promiseConnect: function () {
|
||||
promise = new Promise(function(resolve, reject) {
|
||||
resolveWebSocketConnect = resolve;
|
||||
rejectWebSocketConnect = reject;
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
on: sinon.stub()
|
||||
});
|
||||
});
|
||||
|
||||
it("should store the session data", function() {
|
||||
sandbox.stub(conversation, "setIncomingSessionData");
|
||||
|
||||
icView = mountTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(conversation.setIncomingSessionData);
|
||||
sinon.assert.calledWithExactly(conversation.setIncomingSessionData,
|
||||
fakeSessionData);
|
||||
});
|
||||
|
||||
it("should setup the websocket connection", function() {
|
||||
icView = mountTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(loop.CallConnectionWebSocket);
|
||||
sinon.assert.calledWithExactly(loop.CallConnectionWebSocket, {
|
||||
callId: "Hello",
|
||||
url: "http://progress.example.com",
|
||||
websocketToken: "7b"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("WebSocket Handling", function() {
|
||||
beforeEach(function() {
|
||||
promise = new Promise(function(resolve, reject) {
|
||||
resolveWebSocketConnect = resolve;
|
||||
rejectWebSocketConnect = reject;
|
||||
});
|
||||
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect").returns(promise);
|
||||
});
|
||||
|
||||
it("should set the state to incoming on success", function(done) {
|
||||
icView = mountTestComponent();
|
||||
resolveWebSocketConnect("incoming");
|
||||
|
||||
promise.then(function () {
|
||||
expect(icView.state.callStatus).eql("incoming");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should set the state to close on success if the progress " +
|
||||
"state is terminated", function(done) {
|
||||
icView = mountTestComponent();
|
||||
resolveWebSocketConnect("terminated");
|
||||
|
||||
promise.then(function () {
|
||||
expect(icView.state.callStatus).eql("close");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// XXX implement me as part of bug 1047410
|
||||
// see https://hg.mozilla.org/integration/fx-team/rev/5d2c69ebb321#l18.259
|
||||
it.skip("should should switch view state to failed", function(done) {
|
||||
icView = mountTestComponent();
|
||||
rejectWebSocketConnect();
|
||||
|
||||
promise.then(function() {}, function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("WebSocket Events", function() {
|
||||
describe("Call cancelled or timed out before acceptance", function() {
|
||||
beforeEach(function() {
|
||||
// Mounting the test component automatically calls the required
|
||||
// setup functions
|
||||
icView = mountTestComponent();
|
||||
promise = new Promise(function(resolve, reject) {
|
||||
resolve();
|
||||
});
|
||||
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect").returns(promise);
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype, "close");
|
||||
});
|
||||
|
||||
describe("progress - terminated (previousState = alerting)", function() {
|
||||
it("should stop alerting", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: WEBSOCKET_REASONS.TIMEOUT
|
||||
}, "alerting");
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should close the websocket", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: WEBSOCKET_REASONS.CLOSED
|
||||
}, "alerting");
|
||||
|
||||
sinon.assert.calledOnce(icView._websocket.close);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should close the window", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: WEBSOCKET_REASONS.ANSWERED_ELSEWHERE
|
||||
}, "alerting");
|
||||
|
||||
sandbox.clock.tick(1);
|
||||
|
||||
sinon.assert.calledOnce(fakeWindow.close);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("progress - terminated (previousState not init" +
|
||||
" nor alerting)",
|
||||
function() {
|
||||
it("should set the state to end", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: WEBSOCKET_REASONS.MEDIA_FAIL
|
||||
}, "connecting");
|
||||
|
||||
expect(icView.state.callStatus).eql("end");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should stop alerting", function(done) {
|
||||
promise.then(function() {
|
||||
icView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: WEBSOCKET_REASONS.MEDIA_FAIL
|
||||
}, "connecting");
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#accept", function() {
|
||||
beforeEach(function() {
|
||||
icView = mountTestComponent();
|
||||
conversation.setIncomingSessionData({
|
||||
sessionId: "sessionId",
|
||||
sessionToken: "sessionToken",
|
||||
apiKey: "apiKey",
|
||||
callType: "callType",
|
||||
callId: "Hello",
|
||||
progressURL: "http://progress.example.com",
|
||||
websocketToken: 123
|
||||
});
|
||||
|
||||
sandbox.stub(icView._websocket, "accept");
|
||||
sandbox.stub(icView.props.conversation, "accepted");
|
||||
});
|
||||
|
||||
it("should initiate the conversation", function() {
|
||||
icView.accept();
|
||||
|
||||
sinon.assert.calledOnce(icView.props.conversation.accepted);
|
||||
});
|
||||
|
||||
it("should notify the websocket of the user acceptance", function() {
|
||||
icView.accept();
|
||||
|
||||
sinon.assert.calledOnce(icView._websocket.accept);
|
||||
});
|
||||
|
||||
it("should stop alerting", function() {
|
||||
icView.accept();
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#decline", function() {
|
||||
beforeEach(function() {
|
||||
icView = mountTestComponent();
|
||||
|
||||
icView._websocket = {
|
||||
decline: sinon.stub(),
|
||||
close: sinon.stub()
|
||||
};
|
||||
conversation.set({
|
||||
windowId: "8699"
|
||||
});
|
||||
conversation.setIncomingSessionData({
|
||||
websocketToken: 123
|
||||
});
|
||||
});
|
||||
|
||||
it("should close the window", function() {
|
||||
icView.decline();
|
||||
|
||||
sandbox.clock.tick(1);
|
||||
|
||||
sinon.assert.calledOnce(fakeWindow.close);
|
||||
});
|
||||
|
||||
it("should stop alerting", function() {
|
||||
icView.decline();
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
|
||||
});
|
||||
|
||||
it("should release callData", function() {
|
||||
icView.decline();
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
|
||||
sinon.assert.calledWithExactly(
|
||||
navigator.mozLoop.calls.clearCallInProgress, "8699");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#blocked", function() {
|
||||
var mozLoop, deleteCallUrlStub;
|
||||
|
||||
beforeEach(function() {
|
||||
icView = mountTestComponent();
|
||||
|
||||
icView._websocket = {
|
||||
decline: sinon.spy(),
|
||||
close: sinon.stub()
|
||||
};
|
||||
|
||||
mozLoop = {
|
||||
LOOP_SESSION_TYPE: {
|
||||
GUEST: 1,
|
||||
FXA: 2
|
||||
}
|
||||
};
|
||||
|
||||
deleteCallUrlStub = sandbox.stub(loop.Client.prototype,
|
||||
"deleteCallUrl");
|
||||
});
|
||||
|
||||
it("should call mozLoop.stopAlerting", function() {
|
||||
icView.declineAndBlock();
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
|
||||
});
|
||||
|
||||
it("should call delete call", function() {
|
||||
sandbox.stub(conversation, "get").withArgs("callToken")
|
||||
.returns("fakeToken")
|
||||
.withArgs("sessionType")
|
||||
.returns(mozLoop.LOOP_SESSION_TYPE.FXA);
|
||||
|
||||
icView.declineAndBlock();
|
||||
|
||||
sinon.assert.calledOnce(deleteCallUrlStub);
|
||||
sinon.assert.calledWithExactly(deleteCallUrlStub,
|
||||
"fakeToken", mozLoop.LOOP_SESSION_TYPE.FXA, sinon.match.func);
|
||||
});
|
||||
|
||||
it("should get callToken from conversation model", function() {
|
||||
sandbox.stub(conversation, "get");
|
||||
icView.declineAndBlock();
|
||||
|
||||
sinon.assert.called(conversation.get);
|
||||
sinon.assert.calledWithExactly(conversation.get, "callToken");
|
||||
sinon.assert.calledWithExactly(conversation.get, "windowId");
|
||||
});
|
||||
|
||||
it("should trigger error handling in case of error", function() {
|
||||
// XXX just logging to console for now
|
||||
var log = sandbox.stub(console, "log");
|
||||
var fakeError = {
|
||||
error: true
|
||||
};
|
||||
deleteCallUrlStub.callsArgWith(2, fakeError);
|
||||
icView.declineAndBlock();
|
||||
|
||||
sinon.assert.calledOnce(log);
|
||||
sinon.assert.calledWithExactly(log, fakeError);
|
||||
});
|
||||
|
||||
it("should close the window", function() {
|
||||
icView.declineAndBlock();
|
||||
|
||||
sandbox.clock.tick(1);
|
||||
|
||||
sinon.assert.calledOnce(fakeWindow.close);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", function() {
|
||||
var fakeSessionData;
|
||||
|
||||
beforeEach(function() {
|
||||
|
||||
fakeSessionData = {
|
||||
sessionId: "sessionId",
|
||||
sessionToken: "sessionToken",
|
||||
apiKey: "apiKey"
|
||||
};
|
||||
|
||||
conversationAppStore.setStoreState({
|
||||
windowData: fakeSessionData
|
||||
});
|
||||
|
||||
sandbox.stub(conversation, "setIncomingSessionData");
|
||||
sandbox.stub(loop, "CallConnectionWebSocket").returns({
|
||||
promiseConnect: function() {
|
||||
return new Promise(function() {});
|
||||
},
|
||||
on: sandbox.spy()
|
||||
});
|
||||
|
||||
icView = mountTestComponent();
|
||||
|
||||
conversation.set("loopToken", "fakeToken");
|
||||
stubComponent(sharedView, "ConversationView");
|
||||
});
|
||||
|
||||
describe("call:accepted", function() {
|
||||
it("should display the ConversationView",
|
||||
function() {
|
||||
conversation.accepted();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(icView,
|
||||
sharedView.ConversationView);
|
||||
});
|
||||
|
||||
it("should set the title to the call identifier", function() {
|
||||
sandbox.stub(conversation, "getCallIdentifier").returns("fakeId");
|
||||
|
||||
conversation.accepted();
|
||||
|
||||
expect(document.title).eql("fakeId");
|
||||
});
|
||||
});
|
||||
|
||||
describe("session:ended", function() {
|
||||
it("should display the feedback view when the call session ends",
|
||||
function() {
|
||||
conversation.trigger("session:ended");
|
||||
|
||||
TestUtils.findRenderedComponentWithType(icView,
|
||||
sharedView.FeedbackView);
|
||||
});
|
||||
});
|
||||
|
||||
describe("session:peer-hungup", function() {
|
||||
it("should display the feedback view when the peer hangs up",
|
||||
function() {
|
||||
conversation.trigger("session:peer-hungup");
|
||||
|
||||
TestUtils.findRenderedComponentWithType(icView,
|
||||
sharedView.FeedbackView);
|
||||
});
|
||||
});
|
||||
|
||||
describe("session:network-disconnected", function() {
|
||||
it("should navigate to call failed when network disconnects",
|
||||
function() {
|
||||
conversation.trigger("session:network-disconnected");
|
||||
|
||||
TestUtils.findRenderedComponentWithType(icView,
|
||||
loop.conversationViews.GenericFailureView);
|
||||
});
|
||||
|
||||
it("should update the conversation window toolbar title",
|
||||
function() {
|
||||
conversation.trigger("session:network-disconnected");
|
||||
|
||||
expect(document.title).eql("generic_failure_title");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Published and Subscribed Streams", function() {
|
||||
beforeEach(function() {
|
||||
icView._websocket = {
|
||||
mediaUp: sinon.spy()
|
||||
};
|
||||
});
|
||||
|
||||
describe("publishStream", function() {
|
||||
it("should not notify the websocket if only one stream is up",
|
||||
function() {
|
||||
conversation.set("publishedStream", true);
|
||||
|
||||
sinon.assert.notCalled(icView._websocket.mediaUp);
|
||||
});
|
||||
|
||||
it("should notify the websocket that media is up if both streams" +
|
||||
"are connected", function() {
|
||||
conversation.set("subscribedStream", true);
|
||||
conversation.set("publishedStream", true);
|
||||
|
||||
sinon.assert.calledOnce(icView._websocket.mediaUp);
|
||||
});
|
||||
});
|
||||
|
||||
describe("subscribedStream", function() {
|
||||
it("should not notify the websocket if only one stream is up",
|
||||
function() {
|
||||
conversation.set("subscribedStream", true);
|
||||
|
||||
sinon.assert.notCalled(icView._websocket.mediaUp);
|
||||
});
|
||||
|
||||
it("should notify the websocket that media is up if both streams" +
|
||||
"are connected", function() {
|
||||
conversation.set("publishedStream", true);
|
||||
conversation.set("subscribedStream", true);
|
||||
|
||||
sinon.assert.calledOnce(icView._websocket.mediaUp);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("AcceptCallView", function() {
|
||||
var view;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user