mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1093787 - Insert an additional view for Loop standalone calls to prompt the user to accept the microphone and camera permissions before starting the call. r=nperriault
This commit is contained in:
parent
b1d71f50ed
commit
ee077adad2
@ -95,6 +95,13 @@ loop.shared.models = (function(l10n) {
|
||||
if (selectedCallType) {
|
||||
this.set("selectedCallType", selectedCallType);
|
||||
}
|
||||
this.trigger("call:outgoing:get-media-privs");
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to indicate that media privileges have been accepted.
|
||||
*/
|
||||
gotMediaPrivs: function() {
|
||||
this.trigger("call:outgoing:setup");
|
||||
},
|
||||
|
||||
|
@ -270,7 +270,80 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* A view for when conversations are pending, displays any messages
|
||||
* and an option cancel button.
|
||||
*/
|
||||
var PendingConversationView = React.createClass({displayName: 'PendingConversationView',
|
||||
propTypes: {
|
||||
callState: React.PropTypes.string.isRequired,
|
||||
// If not supplied, the cancel button is not displayed.
|
||||
cancelCallback: React.PropTypes.func
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var cancelButtonClasses = React.addons.classSet({
|
||||
btn: true,
|
||||
"btn-large": true,
|
||||
"btn-cancel": true,
|
||||
hide: !this.props.cancelCallback
|
||||
});
|
||||
|
||||
return (
|
||||
React.DOM.div({className: "container"},
|
||||
React.DOM.div({className: "container-box"},
|
||||
React.DOM.header({className: "pending-header header-box"},
|
||||
ConversationBranding(null)
|
||||
),
|
||||
|
||||
React.DOM.div({id: "cameraPreview"}),
|
||||
|
||||
React.DOM.div({id: "messages"}),
|
||||
|
||||
React.DOM.p({className: "standalone-btn-label"},
|
||||
this.props.callState
|
||||
),
|
||||
|
||||
React.DOM.div({className: "btn-pending-cancel-group btn-group"},
|
||||
React.DOM.div({className: "flex-padding-1"}),
|
||||
React.DOM.button({className: cancelButtonClasses,
|
||||
onClick: this.props.cancelCallback},
|
||||
React.DOM.span({className: "standalone-call-btn-text"},
|
||||
mozL10n.get("initiate_call_cancel_button")
|
||||
)
|
||||
),
|
||||
React.DOM.div({className: "flex-padding-1"})
|
||||
)
|
||||
),
|
||||
ConversationFooter(null)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* View displayed whilst the get user media prompt is being displayed. Indicates
|
||||
* to the user to accept the prompt.
|
||||
*/
|
||||
var GumPromptConversationView = React.createClass({displayName: 'GumPromptConversationView',
|
||||
render: function() {
|
||||
var callState = mozL10n.get("call_progress_getting_media_description", {
|
||||
clientShortname: mozL10n.get("clientShortname2")
|
||||
});
|
||||
document.title = mozL10n.get("standalone_title_with_status", {
|
||||
clientShortname: mozL10n.get("clientShortname2"),
|
||||
currentStatus: mozL10n.get("call_progress_getting_media_title")
|
||||
});
|
||||
|
||||
return PendingConversationView({callState: callState});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* View displayed waiting for a call to be connected. Updates the display
|
||||
* once the websocket shows that the callee is being alerted.
|
||||
*/
|
||||
var WaitingConversationView = React.createClass({displayName: 'WaitingConversationView',
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
getInitialState: function() {
|
||||
@ -306,33 +379,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
document.title = mozL10n.get("standalone_title_with_status",
|
||||
{clientShortname: mozL10n.get("clientShortname2"),
|
||||
currentStatus: mozL10n.get(callStateStringEntityName)});
|
||||
|
||||
return (
|
||||
React.DOM.div({className: "container"},
|
||||
React.DOM.div({className: "container-box"},
|
||||
React.DOM.header({className: "pending-header header-box"},
|
||||
ConversationBranding(null)
|
||||
),
|
||||
|
||||
React.DOM.div({id: "cameraPreview"}),
|
||||
|
||||
React.DOM.div({id: "messages"}),
|
||||
|
||||
React.DOM.p({className: "standalone-btn-label"},
|
||||
callState
|
||||
),
|
||||
|
||||
React.DOM.div({className: "btn-pending-cancel-group btn-group"},
|
||||
React.DOM.div({className: "flex-padding-1"}),
|
||||
React.DOM.button({className: "btn btn-large btn-cancel",
|
||||
onClick: this._cancelOutgoingCall},
|
||||
React.DOM.span({className: "standalone-call-btn-text"},
|
||||
mozL10n.get("initiate_call_cancel_button")
|
||||
)
|
||||
),
|
||||
React.DOM.div({className: "flex-padding-1"})
|
||||
)
|
||||
),
|
||||
ConversationFooter(null)
|
||||
PendingConversationView({
|
||||
callState: callState,
|
||||
cancelCallback: this._cancelOutgoingCall}
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -458,15 +509,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*/
|
||||
startCall: function(callType) {
|
||||
return function() {
|
||||
multiplexGum.getPermsAndCacheMedia({audio:true, video:true},
|
||||
function(localStream) {
|
||||
this.props.conversation.setupOutgoingCall(callType);
|
||||
this.setState({disableCallButton: true});
|
||||
}.bind(this),
|
||||
function(errorCode) {
|
||||
multiplexGum.reset();
|
||||
}.bind(this)
|
||||
);
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
@ -627,6 +671,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
|
||||
componentDidMount: function() {
|
||||
this.props.conversation.on("call:outgoing", this.startCall, this);
|
||||
this.props.conversation.on("call:outgoing:get-media-privs", this.getMediaPrivs, this);
|
||||
this.props.conversation.on("call:outgoing:setup", this.setupOutgoingCall, this);
|
||||
this.props.conversation.on("change:publishedStream", this._checkConnected, this);
|
||||
this.props.conversation.on("change:subscribedStream", this._checkConnected, this);
|
||||
@ -674,8 +719,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
)
|
||||
);
|
||||
}
|
||||
case "gumPrompt": {
|
||||
return GumPromptConversationView(null);
|
||||
}
|
||||
case "pending": {
|
||||
return PendingConversationView({websocket: this._websocket});
|
||||
return WaitingConversationView({websocket: this._websocket});
|
||||
}
|
||||
case "connected": {
|
||||
document.title = mozL10n.get("standalone_title_with_status",
|
||||
@ -774,6 +822,22 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Asks the user for the media privileges, handling the result appropriately.
|
||||
*/
|
||||
getMediaPrivs: function() {
|
||||
this.setState({callStatus: "gumPrompt"});
|
||||
multiplexGum.getPermsAndCacheMedia({audio:true, video:true},
|
||||
function(localStream) {
|
||||
this.props.conversation.gotMediaPrivs();
|
||||
}.bind(this),
|
||||
function(errorCode) {
|
||||
multiplexGum.reset();
|
||||
this.setState({callStatus: "failure"});
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Actually starts the call.
|
||||
*/
|
||||
@ -866,6 +930,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
* Handles ending a call by resetting the view to the start state.
|
||||
*/
|
||||
_endCall: function() {
|
||||
multiplexGum.reset();
|
||||
|
||||
if (this.state.callStatus !== "failure") {
|
||||
this.setState({callStatus: "end"});
|
||||
}
|
||||
@ -1050,6 +1116,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
return {
|
||||
CallUrlExpiredView: CallUrlExpiredView,
|
||||
PendingConversationView: PendingConversationView,
|
||||
GumPromptConversationView: GumPromptConversationView,
|
||||
WaitingConversationView: WaitingConversationView,
|
||||
StartConversationView: StartConversationView,
|
||||
FailedConversationView: FailedConversationView,
|
||||
OutgoingConversationView: OutgoingConversationView,
|
||||
|
@ -270,7 +270,80 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* A view for when conversations are pending, displays any messages
|
||||
* and an option cancel button.
|
||||
*/
|
||||
var PendingConversationView = React.createClass({
|
||||
propTypes: {
|
||||
callState: React.PropTypes.string.isRequired,
|
||||
// If not supplied, the cancel button is not displayed.
|
||||
cancelCallback: React.PropTypes.func
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var cancelButtonClasses = React.addons.classSet({
|
||||
btn: true,
|
||||
"btn-large": true,
|
||||
"btn-cancel": true,
|
||||
hide: !this.props.cancelCallback
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="container-box">
|
||||
<header className="pending-header header-box">
|
||||
<ConversationBranding />
|
||||
</header>
|
||||
|
||||
<div id="cameraPreview" />
|
||||
|
||||
<div id="messages" />
|
||||
|
||||
<p className="standalone-btn-label">
|
||||
{this.props.callState}
|
||||
</p>
|
||||
|
||||
<div className="btn-pending-cancel-group btn-group">
|
||||
<div className="flex-padding-1" />
|
||||
<button className={cancelButtonClasses}
|
||||
onClick={this.props.cancelCallback} >
|
||||
<span className="standalone-call-btn-text">
|
||||
{mozL10n.get("initiate_call_cancel_button")}
|
||||
</span>
|
||||
</button>
|
||||
<div className="flex-padding-1" />
|
||||
</div>
|
||||
</div>
|
||||
<ConversationFooter />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* View displayed whilst the get user media prompt is being displayed. Indicates
|
||||
* to the user to accept the prompt.
|
||||
*/
|
||||
var GumPromptConversationView = React.createClass({
|
||||
render: function() {
|
||||
var callState = mozL10n.get("call_progress_getting_media_description", {
|
||||
clientShortname: mozL10n.get("clientShortname2")
|
||||
});
|
||||
document.title = mozL10n.get("standalone_title_with_status", {
|
||||
clientShortname: mozL10n.get("clientShortname2"),
|
||||
currentStatus: mozL10n.get("call_progress_getting_media_title")
|
||||
});
|
||||
|
||||
return <PendingConversationView callState={callState}/>;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* View displayed waiting for a call to be connected. Updates the display
|
||||
* once the websocket shows that the callee is being alerted.
|
||||
*/
|
||||
var WaitingConversationView = React.createClass({
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
getInitialState: function() {
|
||||
@ -306,34 +379,12 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
document.title = mozL10n.get("standalone_title_with_status",
|
||||
{clientShortname: mozL10n.get("clientShortname2"),
|
||||
currentStatus: mozL10n.get(callStateStringEntityName)});
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="container-box">
|
||||
<header className="pending-header header-box">
|
||||
<ConversationBranding />
|
||||
</header>
|
||||
|
||||
<div id="cameraPreview" />
|
||||
|
||||
<div id="messages" />
|
||||
|
||||
<p className="standalone-btn-label">
|
||||
{callState}
|
||||
</p>
|
||||
|
||||
<div className="btn-pending-cancel-group btn-group">
|
||||
<div className="flex-padding-1" />
|
||||
<button className="btn btn-large btn-cancel"
|
||||
onClick={this._cancelOutgoingCall} >
|
||||
<span className="standalone-call-btn-text">
|
||||
{mozL10n.get("initiate_call_cancel_button")}
|
||||
</span>
|
||||
</button>
|
||||
<div className="flex-padding-1" />
|
||||
</div>
|
||||
</div>
|
||||
<ConversationFooter />
|
||||
</div>
|
||||
<PendingConversationView
|
||||
callState={callState}
|
||||
cancelCallback={this._cancelOutgoingCall}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -458,15 +509,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*/
|
||||
startCall: function(callType) {
|
||||
return function() {
|
||||
multiplexGum.getPermsAndCacheMedia({audio:true, video:true},
|
||||
function(localStream) {
|
||||
this.props.conversation.setupOutgoingCall(callType);
|
||||
this.setState({disableCallButton: true});
|
||||
}.bind(this),
|
||||
function(errorCode) {
|
||||
multiplexGum.reset();
|
||||
}.bind(this)
|
||||
);
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
@ -627,6 +671,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
|
||||
componentDidMount: function() {
|
||||
this.props.conversation.on("call:outgoing", this.startCall, this);
|
||||
this.props.conversation.on("call:outgoing:get-media-privs", this.getMediaPrivs, this);
|
||||
this.props.conversation.on("call:outgoing:setup", this.setupOutgoingCall, this);
|
||||
this.props.conversation.on("change:publishedStream", this._checkConnected, this);
|
||||
this.props.conversation.on("change:subscribedStream", this._checkConnected, this);
|
||||
@ -674,8 +719,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "gumPrompt": {
|
||||
return <GumPromptConversationView />;
|
||||
}
|
||||
case "pending": {
|
||||
return <PendingConversationView websocket={this._websocket} />;
|
||||
return <WaitingConversationView websocket={this._websocket} />;
|
||||
}
|
||||
case "connected": {
|
||||
document.title = mozL10n.get("standalone_title_with_status",
|
||||
@ -774,6 +822,22 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Asks the user for the media privileges, handling the result appropriately.
|
||||
*/
|
||||
getMediaPrivs: function() {
|
||||
this.setState({callStatus: "gumPrompt"});
|
||||
multiplexGum.getPermsAndCacheMedia({audio:true, video:true},
|
||||
function(localStream) {
|
||||
this.props.conversation.gotMediaPrivs();
|
||||
}.bind(this),
|
||||
function(errorCode) {
|
||||
multiplexGum.reset();
|
||||
this.setState({callStatus: "failure"});
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Actually starts the call.
|
||||
*/
|
||||
@ -866,6 +930,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
* Handles ending a call by resetting the view to the start state.
|
||||
*/
|
||||
_endCall: function() {
|
||||
multiplexGum.reset();
|
||||
|
||||
if (this.state.callStatus !== "failure") {
|
||||
this.setState({callStatus: "end"});
|
||||
}
|
||||
@ -1050,6 +1116,8 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
return {
|
||||
CallUrlExpiredView: CallUrlExpiredView,
|
||||
PendingConversationView: PendingConversationView,
|
||||
GumPromptConversationView: GumPromptConversationView,
|
||||
WaitingConversationView: WaitingConversationView,
|
||||
StartConversationView: StartConversationView,
|
||||
FailedConversationView: FailedConversationView,
|
||||
OutgoingConversationView: OutgoingConversationView,
|
||||
|
@ -58,6 +58,8 @@ vendor_alttext={{vendorShortname}} logo
|
||||
|
||||
## LOCALIZATION NOTE (call_url_creation_date_label): Example output: (from May 26, 2014)
|
||||
call_url_creation_date_label=(from {{call_url_creation_date}})
|
||||
call_progress_getting_media_description={{clientShortname}} requires access to your camera and microphone.
|
||||
call_progress_getting_media_title=Waiting for media…
|
||||
call_progress_connecting_description=Connecting…
|
||||
call_progress_ringing_description=Ringing…
|
||||
fxos_app_needed=Please install the {{fxosAppName}} app from the Firefox Marketplace.
|
||||
|
@ -91,12 +91,22 @@ describe("loop.shared.models", function() {
|
||||
expect(conversation.get("selectedCallType")).eql("audio-video");
|
||||
});
|
||||
|
||||
it("should trigger a `call:outgoing:get-media-privs` event", function(done) {
|
||||
conversation.once("call:outgoing:get-media-privs", function() {
|
||||
done();
|
||||
});
|
||||
|
||||
conversation.setupOutgoingCall();
|
||||
});
|
||||
});
|
||||
|
||||
describe("#gotMediaPrivs", function() {
|
||||
it("should trigger a `call:outgoing:setup` event", function(done) {
|
||||
conversation.once("call:outgoing:setup", function() {
|
||||
done();
|
||||
});
|
||||
|
||||
conversation.setupOutgoingCall();
|
||||
conversation.gotMediaPrivs();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -369,6 +369,16 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
|
||||
describe("session:ended", function() {
|
||||
it("should call multiplexGum.reset", function() {
|
||||
var multiplexGum = new standaloneMedia._MultiplexGum();
|
||||
standaloneMedia.setSingleton(multiplexGum);
|
||||
sandbox.stub(standaloneMedia._MultiplexGum.prototype, "reset");
|
||||
|
||||
conversation.trigger("session:ended");
|
||||
|
||||
sinon.assert.calledOnce(multiplexGum.reset);
|
||||
});
|
||||
|
||||
it("should display the StartConversationView", function() {
|
||||
conversation.trigger("session:ended");
|
||||
|
||||
@ -474,14 +484,14 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
|
||||
it("should display the FailedConversationView", function() {
|
||||
conversation.setupOutgoingCall();
|
||||
ocView.setupOutgoingCall();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ocView,
|
||||
loop.webapp.FailedConversationView);
|
||||
});
|
||||
|
||||
it("should display an error", function() {
|
||||
conversation.setupOutgoingCall();
|
||||
ocView.setupOutgoingCall();
|
||||
|
||||
sinon.assert.calledOnce(notifications.errorL10n);
|
||||
});
|
||||
@ -494,7 +504,8 @@ describe("loop.webapp", function() {
|
||||
|
||||
it("should call requestCallInfo on the client",
|
||||
function() {
|
||||
conversation.setupOutgoingCall("audio-video");
|
||||
conversation.set("selectedCallType", "audio-video");
|
||||
ocView.setupOutgoingCall();
|
||||
|
||||
sinon.assert.calledOnce(client.requestCallInfo);
|
||||
sinon.assert.calledWith(client.requestCallInfo, "fakeToken",
|
||||
@ -506,7 +517,7 @@ describe("loop.webapp", function() {
|
||||
function() {
|
||||
client.requestCallInfo.callsArgWith(2, {errno: 105});
|
||||
|
||||
conversation.setupOutgoingCall();
|
||||
ocView.setupOutgoingCall();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ocView,
|
||||
loop.webapp.CallUrlExpiredView);
|
||||
@ -516,7 +527,7 @@ describe("loop.webapp", function() {
|
||||
function() {
|
||||
client.requestCallInfo.callsArgWith(2, {errno: 104});
|
||||
|
||||
conversation.setupOutgoingCall();
|
||||
ocView.setupOutgoingCall();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ocView,
|
||||
loop.webapp.FailedConversationView);
|
||||
@ -525,7 +536,7 @@ describe("loop.webapp", function() {
|
||||
it("should notify the user on any other error", function() {
|
||||
client.requestCallInfo.callsArgWith(2, {errno: 104});
|
||||
|
||||
conversation.setupOutgoingCall();
|
||||
ocView.setupOutgoingCall();
|
||||
|
||||
sinon.assert.calledOnce(notifications.errorL10n);
|
||||
});
|
||||
@ -534,7 +545,7 @@ describe("loop.webapp", function() {
|
||||
"are successfully received", function() {
|
||||
client.requestCallInfo.callsArgWith(2, null, fakeSessionData);
|
||||
|
||||
conversation.setupOutgoingCall();
|
||||
ocView.setupOutgoingCall();
|
||||
|
||||
sinon.assert.calledOnce(conversation.outgoing);
|
||||
sinon.assert.calledWithExactly(conversation.outgoing, fakeSessionData);
|
||||
@ -542,6 +553,52 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getMediaPrivs", function() {
|
||||
var multiplexGum;
|
||||
|
||||
beforeEach(function() {
|
||||
multiplexGum = new standaloneMedia._MultiplexGum();
|
||||
standaloneMedia.setSingleton(multiplexGum);
|
||||
sandbox.stub(standaloneMedia._MultiplexGum.prototype, "reset");
|
||||
|
||||
sandbox.stub(conversation, "gotMediaPrivs");
|
||||
});
|
||||
|
||||
it("should call getPermsAndCacheMedia", function() {
|
||||
conversation.trigger("call:outgoing:get-media-privs");
|
||||
|
||||
sinon.assert.calledOnce(stubGetPermsAndCacheMedia);
|
||||
});
|
||||
|
||||
it("should call gotMediaPrevs on the model when successful", function() {
|
||||
stubGetPermsAndCacheMedia.callsArgWith(1, {});
|
||||
|
||||
conversation.trigger("call:outgoing:get-media-privs");
|
||||
|
||||
sinon.assert.calledOnce(conversation.gotMediaPrivs);
|
||||
});
|
||||
|
||||
it("should call multiplexGum.reset when getPermsAndCacheMedia fails",
|
||||
function() {
|
||||
stubGetPermsAndCacheMedia.callsArgWith(2, "FAKE_ERROR");
|
||||
|
||||
conversation.trigger("call:outgoing:get-media-privs");
|
||||
|
||||
sinon.assert.calledOnce(multiplexGum.reset);
|
||||
});
|
||||
|
||||
it("should set state to `failure` when getPermsAndCacheMedia fails",
|
||||
function() {
|
||||
stubGetPermsAndCacheMedia.callsArgWith(2, "FAKE_ERROR");
|
||||
|
||||
conversation.trigger("call:outgoing:get-media-privs");
|
||||
|
||||
expect(ocView.state.callStatus).eql("failure");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe("FailedConversationView", function() {
|
||||
@ -693,7 +750,7 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("PendingConversationView", function() {
|
||||
describe("WaitingConversationView", function() {
|
||||
var view, websocket, fakeAudio;
|
||||
|
||||
beforeEach(function() {
|
||||
@ -713,7 +770,7 @@ describe("loop.webapp", function() {
|
||||
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
||||
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.PendingConversationView({
|
||||
loop.webapp.WaitingConversationView({
|
||||
websocket: websocket
|
||||
})
|
||||
);
|
||||
@ -802,25 +859,6 @@ describe("loop.webapp", function() {
|
||||
client: standaloneClientStub
|
||||
})
|
||||
);
|
||||
|
||||
// default to succeeding with a null local media object
|
||||
stubGetPermsAndCacheMedia.callsArgWith(1, {});
|
||||
});
|
||||
|
||||
it("should fire multiplexGum.reset when getPermsAndCacheMedia calls" +
|
||||
" back an error",
|
||||
function() {
|
||||
var setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
|
||||
var multiplexGum = new standaloneMedia._MultiplexGum();
|
||||
standaloneMedia.setSingleton(multiplexGum);
|
||||
sandbox.stub(standaloneMedia._MultiplexGum.prototype, "reset");
|
||||
stubGetPermsAndCacheMedia.callsArgWith(2, "FAKE_ERROR");
|
||||
|
||||
var button = view.getDOMNode().querySelector(".btn-accept");
|
||||
React.addons.TestUtils.Simulate.click(button);
|
||||
|
||||
sinon.assert.calledOnce(multiplexGum.reset);
|
||||
sinon.assert.calledWithExactly(multiplexGum.reset);
|
||||
});
|
||||
|
||||
it("should start the audio-video conversation establishment process",
|
||||
|
@ -28,7 +28,8 @@
|
||||
var UnsupportedBrowserView = loop.webapp.UnsupportedBrowserView;
|
||||
var UnsupportedDeviceView = loop.webapp.UnsupportedDeviceView;
|
||||
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
|
||||
var PendingConversationView = loop.webapp.PendingConversationView;
|
||||
var GumPromptConversationView = loop.webapp.GumPromptConversationView;
|
||||
var WaitingConversationView = loop.webapp.WaitingConversationView;
|
||||
var StartConversationView = loop.webapp.StartConversationView;
|
||||
var FailedConversationView = loop.webapp.FailedConversationView;
|
||||
var EndedConversationView = loop.webapp.EndedConversationView;
|
||||
@ -326,16 +327,24 @@
|
||||
)
|
||||
),
|
||||
|
||||
Section({name: "PendingConversationView"},
|
||||
Example({summary: "Pending conversation view (connecting)", dashed: "true"},
|
||||
Section({name: "GumPromptConversationView"},
|
||||
Example({summary: "Gum Prompt conversation view", dashed: "true"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
PendingConversationView({websocket: mockWebSocket,
|
||||
GumPromptConversationView(null)
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
Section({name: "WaitingConversationView"},
|
||||
Example({summary: "Waiting conversation view (connecting)", dashed: "true"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
WaitingConversationView({websocket: mockWebSocket,
|
||||
dispatcher: dispatcher})
|
||||
)
|
||||
),
|
||||
Example({summary: "Pending conversation view (ringing)", dashed: "true"},
|
||||
Example({summary: "Waiting conversation view (ringing)", dashed: "true"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
PendingConversationView({websocket: mockWebSocket,
|
||||
WaitingConversationView({websocket: mockWebSocket,
|
||||
dispatcher: dispatcher,
|
||||
callState: "ringing"})
|
||||
)
|
||||
|
@ -28,7 +28,8 @@
|
||||
var UnsupportedBrowserView = loop.webapp.UnsupportedBrowserView;
|
||||
var UnsupportedDeviceView = loop.webapp.UnsupportedDeviceView;
|
||||
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
|
||||
var PendingConversationView = loop.webapp.PendingConversationView;
|
||||
var GumPromptConversationView = loop.webapp.GumPromptConversationView;
|
||||
var WaitingConversationView = loop.webapp.WaitingConversationView;
|
||||
var StartConversationView = loop.webapp.StartConversationView;
|
||||
var FailedConversationView = loop.webapp.FailedConversationView;
|
||||
var EndedConversationView = loop.webapp.EndedConversationView;
|
||||
@ -326,16 +327,24 @@
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<Section name="PendingConversationView">
|
||||
<Example summary="Pending conversation view (connecting)" dashed="true">
|
||||
<Section name="GumPromptConversationView">
|
||||
<Example summary="Gum Prompt conversation view" dashed="true">
|
||||
<div className="standalone">
|
||||
<PendingConversationView websocket={mockWebSocket}
|
||||
<GumPromptConversationView />
|
||||
</div>
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
<Section name="WaitingConversationView">
|
||||
<Example summary="Waiting conversation view (connecting)" dashed="true">
|
||||
<div className="standalone">
|
||||
<WaitingConversationView websocket={mockWebSocket}
|
||||
dispatcher={dispatcher} />
|
||||
</div>
|
||||
</Example>
|
||||
<Example summary="Pending conversation view (ringing)" dashed="true">
|
||||
<Example summary="Waiting conversation view (ringing)" dashed="true">
|
||||
<div className="standalone">
|
||||
<PendingConversationView websocket={mockWebSocket}
|
||||
<WaitingConversationView websocket={mockWebSocket}
|
||||
dispatcher={dispatcher}
|
||||
callState="ringing"/>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user