mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1073410 - get gUM perms earlier for Loop calls (paired with jaws), r=jaws,me
This commit is contained in:
parent
dbfd76eadc
commit
de0015f514
@ -27,6 +27,7 @@
|
|||||||
window.OTProperties.configURL = window.OTProperties.assetURL + 'js/dynamic_config.min.js';
|
window.OTProperties.configURL = window.OTProperties.assetURL + 'js/dynamic_config.min.js';
|
||||||
window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css';
|
window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css';
|
||||||
</script>
|
</script>
|
||||||
|
<script type="text/javascript" src="js/multiplexGum.js"></script>
|
||||||
<script type="text/javascript" src="shared/libs/sdk.js"></script>
|
<script type="text/javascript" src="shared/libs/sdk.js"></script>
|
||||||
<script type="text/javascript" src="libs/l10n-gaia-02ca67948fe8.js"></script>
|
<script type="text/javascript" src="libs/l10n-gaia-02ca67948fe8.js"></script>
|
||||||
<script type="text/javascript" src="shared/libs/react-0.11.2.js"></script>
|
<script type="text/javascript" src="shared/libs/react-0.11.2.js"></script>
|
||||||
|
150
browser/components/loop/standalone/content/js/multiplexGum.js
Normal file
150
browser/components/loop/standalone/content/js/multiplexGum.js
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|
||||||
|
var loop = loop || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monkeypatch getUserMedia in a way that prevents additional camera and
|
||||||
|
* microphone prompts, at the cost of ignoring all constraints other than
|
||||||
|
* the first set passed in.
|
||||||
|
*
|
||||||
|
* The first call to navigator.getUserMedia (also now aliased to
|
||||||
|
* multiplexGum.getPermsAndCacheMedia to allow for explicit calling code)
|
||||||
|
* will cause the underlying gUM implementation to be called.
|
||||||
|
*
|
||||||
|
* While permission is pending, subsequent calls will result in the callbacks
|
||||||
|
* being queued. Once the call succeeds or fails, all queued success or
|
||||||
|
* failure callbacks will be invoked. Subsequent calls to either function will
|
||||||
|
* cause the success or failure callback to be invoked immediately.
|
||||||
|
*/
|
||||||
|
loop.standaloneMedia = (function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function patchSymbolIfExtant(objectName, propertyName, replacement) {
|
||||||
|
var object;
|
||||||
|
if (window[objectName]) {
|
||||||
|
object = window[objectName];
|
||||||
|
}
|
||||||
|
if (object && object[propertyName]) {
|
||||||
|
object[propertyName] = replacement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// originalGum _must_ be on navigator; otherwise things blow up
|
||||||
|
navigator.originalGum = navigator.getUserMedia ||
|
||||||
|
navigator.mozGetUserMedia ||
|
||||||
|
navigator.webkitGetUserMedia ||
|
||||||
|
(window["TBPlugin"] && TBPlugin.getUserMedia);
|
||||||
|
|
||||||
|
function _MultiplexGum() {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
_MultiplexGum.prototype = {
|
||||||
|
/**
|
||||||
|
* @see The docs at the top of this file for overall semantics,
|
||||||
|
* & http://developer.mozilla.org/en-US/docs/NavigatorUserMedia.getUserMedia
|
||||||
|
* for params, since this is intended to be purely a passthrough to gUM.
|
||||||
|
*/
|
||||||
|
getPermsAndCacheMedia: function(constraints, onSuccess, onError) {
|
||||||
|
function handleResult(callbacks, param) {
|
||||||
|
// Operate on a copy of the array in case any of the callbacks
|
||||||
|
// calls reset, which would cause an infinite-recursion.
|
||||||
|
this.userMedia.successCallbacks = [];
|
||||||
|
this.userMedia.errorCallbacks = [];
|
||||||
|
callbacks.forEach(function(cb) {
|
||||||
|
if (typeof cb == "function") {
|
||||||
|
cb(param);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function handleSuccess(localStream) {
|
||||||
|
this.userMedia.pending = false;
|
||||||
|
this.userMedia.localStream = localStream;
|
||||||
|
this.userMedia.error = null;
|
||||||
|
handleResult.call(this, this.userMedia.successCallbacks.slice(0), localStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(error) {
|
||||||
|
this.userMedia.pending = false;
|
||||||
|
this.userMedia.error = error;
|
||||||
|
handleResult.call(this, this.userMedia.errorCallbacks.slice(0), error);
|
||||||
|
this.error = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.userMedia.localStream &&
|
||||||
|
this.userMedia.localStream.ended) {
|
||||||
|
this.userMedia.localStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.userMedia.errorCallbacks.push(onError);
|
||||||
|
this.userMedia.successCallbacks.push(onSuccess);
|
||||||
|
|
||||||
|
if (this.userMedia.localStream) {
|
||||||
|
handleSuccess.call(this, this.userMedia.localStream);
|
||||||
|
return;
|
||||||
|
} else if (this.userMedia.error) {
|
||||||
|
handleError.call(this, this.userMedia.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.userMedia.pending) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.userMedia.pending = true;
|
||||||
|
|
||||||
|
navigator.originalGum(constraints, handleSuccess.bind(this),
|
||||||
|
handleError.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the cached permissions, callbacks, and media to their default
|
||||||
|
* state and call any error callbacks to let any waiting callers know
|
||||||
|
* not to ever expect any more callbacks. We use "PERMISSION_DENIED",
|
||||||
|
* for lack of a better, more specific gUM code that callers are likely
|
||||||
|
* to be prepared to handle.
|
||||||
|
*/
|
||||||
|
reset: function() {
|
||||||
|
// When called from the ctor, userMedia is not created yet.
|
||||||
|
if (this.userMedia) {
|
||||||
|
this.userMedia.errorCallbacks.forEach(function(cb) {
|
||||||
|
if (typeof cb == "function") {
|
||||||
|
cb("PERMISSION_DENIED");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (this.userMedia.localStream &&
|
||||||
|
typeof this.userMedia.localStream.stop == "function") {
|
||||||
|
this.userMedia.localStream.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.userMedia = {
|
||||||
|
error: null,
|
||||||
|
localStream: null,
|
||||||
|
pending: false,
|
||||||
|
errorCallbacks: [],
|
||||||
|
successCallbacks: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var singletonMultiplexGum = new _MultiplexGum();
|
||||||
|
function myGetUserMedia() {
|
||||||
|
// This function is needed to pull in the instance
|
||||||
|
// of the singleton for tests to overwrite the used instance.
|
||||||
|
singletonMultiplexGum.getPermsAndCacheMedia.apply(singletonMultiplexGum, arguments);
|
||||||
|
};
|
||||||
|
patchSymbolIfExtant("navigator", "mozGetUserMedia", myGetUserMedia);
|
||||||
|
patchSymbolIfExtant("navigator", "webkitGetUserMedia", myGetUserMedia);
|
||||||
|
patchSymbolIfExtant("navigator", "getUserMedia", myGetUserMedia);
|
||||||
|
patchSymbolIfExtant("TBPlugin", "getUserMedia", myGetUserMedia);
|
||||||
|
|
||||||
|
return {
|
||||||
|
multiplexGum: singletonMultiplexGum,
|
||||||
|
_MultiplexGum: _MultiplexGum,
|
||||||
|
setSingleton: function(singleton) {
|
||||||
|
singletonMultiplexGum = singleton;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})();
|
@ -19,11 +19,14 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
var sharedViews = loop.shared.views;
|
var sharedViews = loop.shared.views;
|
||||||
var sharedUtils = loop.shared.utils;
|
var sharedUtils = loop.shared.utils;
|
||||||
|
|
||||||
|
var multiplexGum = loop.standaloneMedia.multiplexGum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Homepage view.
|
* Homepage view.
|
||||||
*/
|
*/
|
||||||
var HomeView = React.createClass({displayName: 'HomeView',
|
var HomeView = React.createClass({displayName: 'HomeView',
|
||||||
render: function() {
|
render: function() {
|
||||||
|
loop.standaloneMedia.multiplexGum.reset();
|
||||||
return (
|
return (
|
||||||
React.DOM.p(null, mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")}))
|
React.DOM.p(null, mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")}))
|
||||||
);
|
);
|
||||||
@ -287,6 +290,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_cancelOutgoingCall: function() {
|
_cancelOutgoingCall: function() {
|
||||||
|
loop.standaloneMedia.multiplexGum.reset();
|
||||||
this.props.websocket.cancel();
|
this.props.websocket.cancel();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -448,8 +452,15 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
*/
|
*/
|
||||||
startCall: function(callType) {
|
startCall: function(callType) {
|
||||||
return function() {
|
return function() {
|
||||||
this.props.conversation.setupOutgoingCall(callType);
|
multiplexGum.getPermsAndCacheMedia({audio:true, video:true},
|
||||||
this.setState({disableCallButton: true});
|
function(localStream) {
|
||||||
|
this.props.conversation.setupOutgoingCall(callType);
|
||||||
|
this.setState({disableCallButton: true});
|
||||||
|
}.bind(this),
|
||||||
|
function(errorCode) {
|
||||||
|
multiplexGum.reset();
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -19,11 +19,14 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
var sharedViews = loop.shared.views;
|
var sharedViews = loop.shared.views;
|
||||||
var sharedUtils = loop.shared.utils;
|
var sharedUtils = loop.shared.utils;
|
||||||
|
|
||||||
|
var multiplexGum = loop.standaloneMedia.multiplexGum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Homepage view.
|
* Homepage view.
|
||||||
*/
|
*/
|
||||||
var HomeView = React.createClass({
|
var HomeView = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
|
loop.standaloneMedia.multiplexGum.reset();
|
||||||
return (
|
return (
|
||||||
<p>{mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")})}</p>
|
<p>{mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")})}</p>
|
||||||
);
|
);
|
||||||
@ -287,6 +290,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_cancelOutgoingCall: function() {
|
_cancelOutgoingCall: function() {
|
||||||
|
loop.standaloneMedia.multiplexGum.reset();
|
||||||
this.props.websocket.cancel();
|
this.props.websocket.cancel();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -448,8 +452,15 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||||||
*/
|
*/
|
||||||
startCall: function(callType) {
|
startCall: function(callType) {
|
||||||
return function() {
|
return function() {
|
||||||
this.props.conversation.setupOutgoingCall(callType);
|
multiplexGum.getPermsAndCacheMedia({audio:true, video:true},
|
||||||
this.setState({disableCallButton: true});
|
function(localStream) {
|
||||||
|
this.props.conversation.setupOutgoingCall(callType);
|
||||||
|
this.setState({disableCallButton: true});
|
||||||
|
}.bind(this),
|
||||||
|
function(errorCode) {
|
||||||
|
multiplexGum.reset();
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -37,11 +37,13 @@
|
|||||||
<script src="../../content/shared/js/views.js"></script>
|
<script src="../../content/shared/js/views.js"></script>
|
||||||
<script src="../../content/shared/js/websocket.js"></script>
|
<script src="../../content/shared/js/websocket.js"></script>
|
||||||
<script src="../../content/shared/js/feedbackApiClient.js"></script>
|
<script src="../../content/shared/js/feedbackApiClient.js"></script>
|
||||||
|
<script src="../../standalone/content/js/multiplexGum.js"></script>
|
||||||
<script src="../../standalone/content/js/standaloneClient.js"></script>
|
<script src="../../standalone/content/js/standaloneClient.js"></script>
|
||||||
<script src="../../standalone/content/js/webapp.js"></script>
|
<script src="../../standalone/content/js/webapp.js"></script>
|
||||||
<!-- Test scripts -->
|
<!-- Test scripts -->
|
||||||
<script src="standalone_client_test.js"></script>
|
<script src="standalone_client_test.js"></script>
|
||||||
<script src="webapp_test.js"></script>
|
<script src="webapp_test.js"></script>
|
||||||
|
<script src="multiplexGum_test.js"></script>
|
||||||
<script>
|
<script>
|
||||||
mocha.run(function () {
|
mocha.run(function () {
|
||||||
$("#mocha").append("<p id='complete'>Complete.</p>");
|
$("#mocha").append("<p id='complete'>Complete.</p>");
|
||||||
|
370
browser/components/loop/test/standalone/multiplexGum_test.js
Normal file
370
browser/components/loop/test/standalone/multiplexGum_test.js
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/*global loop, sinon, it, beforeEach, afterEach, describe*/
|
||||||
|
|
||||||
|
var expect = chai.expect;
|
||||||
|
|
||||||
|
describe("loop.standaloneMedia._MultiplexGum", function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var defaultGum =
|
||||||
|
navigator.getUserMedia ||
|
||||||
|
navigator.mozGetUserMedia ||
|
||||||
|
navigator.webkitGetUserMedia ||
|
||||||
|
(window["TBPlugin"] && TBPlugin.getUserMedia);
|
||||||
|
|
||||||
|
var sandbox;
|
||||||
|
var multiplexGum;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
multiplexGum = new loop.standaloneMedia._MultiplexGum();
|
||||||
|
loop.standaloneMedia.setSingleton(multiplexGum);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#constructor", function() {
|
||||||
|
it("pending should default to false", function() {
|
||||||
|
expect(multiplexGum.userMedia.pending).to.equal(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("default getUserMedia", function() {
|
||||||
|
it("should call getPermsAndCacheMedia", function() {
|
||||||
|
var fakeOptions = {audio: true, video: true};
|
||||||
|
var successCB = function() {};
|
||||||
|
var errorCB = function() {};
|
||||||
|
sandbox.stub(navigator, "originalGum");
|
||||||
|
sandbox.stub(loop.standaloneMedia._MultiplexGum.prototype,
|
||||||
|
"getPermsAndCacheMedia");
|
||||||
|
multiplexGum = new loop.standaloneMedia._MultiplexGum();
|
||||||
|
|
||||||
|
defaultGum(fakeOptions, successCB, errorCB);
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(multiplexGum.getPermsAndCacheMedia);
|
||||||
|
sinon.assert.calledWithExactly(multiplexGum.getPermsAndCacheMedia,
|
||||||
|
fakeOptions, successCB, errorCB);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#getPermsAndCacheMedia", function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
sandbox.stub(navigator, "originalGum");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should change pending to true", function() {
|
||||||
|
multiplexGum.getPermsAndCacheMedia();
|
||||||
|
|
||||||
|
expect(multiplexGum.userMedia.pending).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call originalGum", function() {
|
||||||
|
multiplexGum.getPermsAndCacheMedia();
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(navigator.originalGum);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reset the pending state when the error callback is called",
|
||||||
|
function(done) {
|
||||||
|
var fakeError = new Error();
|
||||||
|
navigator.originalGum.callsArgWith(2, fakeError);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, null, function onError(error) {
|
||||||
|
expect(multiplexGum.userMedia.pending).to.equal(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reset the pending state when the success callback is called",
|
||||||
|
function(done) {
|
||||||
|
var fakeLocalStream = {};
|
||||||
|
navigator.originalGum.callsArgWith(1, fakeLocalStream);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null,
|
||||||
|
function onSuccess(localStream) {
|
||||||
|
expect(multiplexGum.userMedia.pending).to.equal(false);
|
||||||
|
done();
|
||||||
|
}, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call the error callback when originalGum calls back an error",
|
||||||
|
function(done) {
|
||||||
|
var fakeError = new Error();
|
||||||
|
navigator.originalGum.callsArgWith(2, fakeError);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, null, function onError(error) {
|
||||||
|
expect(error).to.eql(fakeError);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should propagate the success callback when originalGum succeeds",
|
||||||
|
function(done) {
|
||||||
|
var fakeLocalStream = {};
|
||||||
|
navigator.originalGum.callsArgWith(1, fakeLocalStream);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null,
|
||||||
|
function onSuccess(localStream) {
|
||||||
|
expect(localStream).to.eql(fakeLocalStream);
|
||||||
|
done();
|
||||||
|
}, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call the success callback when the stream is cached",
|
||||||
|
function(done) {
|
||||||
|
var fakeLocalStream = {};
|
||||||
|
multiplexGum.userMedia.localStream = fakeLocalStream;
|
||||||
|
sinon.assert.notCalled(navigator.originalGum);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null,
|
||||||
|
function onSuccess(localStream) {
|
||||||
|
expect(localStream).to.eql(fakeLocalStream);
|
||||||
|
done();
|
||||||
|
}, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call the error callback when an error is cached",
|
||||||
|
function(done) {
|
||||||
|
var fakeError = new Error();
|
||||||
|
multiplexGum.userMedia.error = fakeError;
|
||||||
|
sinon.assert.notCalled(navigator.originalGum);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, null, function onError(error) {
|
||||||
|
expect(error).to.eql(fakeError);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should clear the error when success is called back", function(done) {
|
||||||
|
var fakeError = new Error();
|
||||||
|
var fakeLocalStream = {};
|
||||||
|
multiplexGum.userMedia.localStream = fakeLocalStream;
|
||||||
|
multiplexGum.userMedia.error = fakeError;
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, function onSuccess(localStream) {
|
||||||
|
expect(multiplexGum.userMedia.error).to.not.eql(fakeError);
|
||||||
|
expect(localStream).to.eql(fakeLocalStream);
|
||||||
|
done();
|
||||||
|
}, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call all success callbacks when success is achieved",
|
||||||
|
function(done) {
|
||||||
|
var fakeLocalStream = {};
|
||||||
|
var calls = 0;
|
||||||
|
// Async is needed so that the callbacks can be queued up.
|
||||||
|
navigator.originalGum.callsArgWithAsync(1, fakeLocalStream);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, function onSuccess(localStream) {
|
||||||
|
calls += 1;
|
||||||
|
expect(localStream).to.eql(fakeLocalStream);
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
expect(multiplexGum.userMedia).to.have.property('pending', true);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, function onSuccess(localStream) {
|
||||||
|
calls += 10;
|
||||||
|
expect(localStream).to.eql(fakeLocalStream);
|
||||||
|
expect(calls).to.equal(11);
|
||||||
|
done();
|
||||||
|
}, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call all error callbacks when error is encountered",
|
||||||
|
function(done) {
|
||||||
|
var fakeError = new Error();
|
||||||
|
var calls = 0;
|
||||||
|
// Async is needed so that the callbacks can be queued up.
|
||||||
|
navigator.originalGum.callsArgWithAsync(2, fakeError);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, null, function onError(error) {
|
||||||
|
calls += 1;
|
||||||
|
expect(error).to.eql(fakeError);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(multiplexGum.userMedia).to.have.property('pending', true);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, null, function onError(error) {
|
||||||
|
calls += 10;
|
||||||
|
expect(error).to.eql(fakeError);
|
||||||
|
expect(calls).to.eql(11);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not call a getPermsAndCacheMedia success callback at the time" +
|
||||||
|
" of gUM success callback fires",
|
||||||
|
function(done) {
|
||||||
|
var fakeLocalStream = {};
|
||||||
|
multiplexGum.userMedia.localStream = fakeLocalStream;
|
||||||
|
navigator.originalGum.callsArgWith(1, fakeLocalStream);
|
||||||
|
var calledOnce = false;
|
||||||
|
var promiseCalledOnce = new Promise(function(resolve, reject) {
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null,
|
||||||
|
function gPACMSuccess(localStream) {
|
||||||
|
expect(localStream).to.eql(fakeLocalStream);
|
||||||
|
expect(multiplexGum.userMedia).to.have.property('pending', false);
|
||||||
|
expect(multiplexGum.userMedia.successCallbacks.length).to.equal(0);
|
||||||
|
if (calledOnce) {
|
||||||
|
sinon.assert.fail("original callback was called twice");
|
||||||
|
}
|
||||||
|
calledOnce = true;
|
||||||
|
resolve();
|
||||||
|
}, function() {
|
||||||
|
sinon.assert.fail("error callback should not have fired");
|
||||||
|
reject();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
promiseCalledOnce.then(function() {
|
||||||
|
defaultGum(null, function gUMSuccess(localStream2) {
|
||||||
|
expect(localStream2).to.eql(fakeLocalStream);
|
||||||
|
expect(multiplexGum.userMedia).to.have.property('pending', false);
|
||||||
|
expect(multiplexGum.userMedia.successCallbacks.length).to.equal(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not call a getPermsAndCacheMedia error callback when the " +
|
||||||
|
" gUM error callback fires",
|
||||||
|
function(done) {
|
||||||
|
var fakeError = "monkeys ate the stream";
|
||||||
|
multiplexGum.userMedia.error = fakeError;
|
||||||
|
navigator.originalGum.callsArgWith(2, fakeError);
|
||||||
|
var calledOnce = false;
|
||||||
|
var promiseCalledOnce = new Promise(function(resolve, reject) {
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, function() {
|
||||||
|
sinon.assert.fail("success callback should not have fired");
|
||||||
|
reject();
|
||||||
|
done();
|
||||||
|
}, function gPACMError(errString) {
|
||||||
|
expect(errString).to.eql(fakeError);
|
||||||
|
expect(multiplexGum.userMedia).to.have.property('pending', false);
|
||||||
|
if (calledOnce) {
|
||||||
|
sinon.assert.fail("original error callback was called twice");
|
||||||
|
}
|
||||||
|
calledOnce = true;
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
promiseCalledOnce.then(function() {
|
||||||
|
defaultGum(null, function() {},
|
||||||
|
function gUMError(errString) {
|
||||||
|
expect(errString).to.eql(fakeError);
|
||||||
|
expect(multiplexGum.userMedia).to.have.property('pending', false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call the success callback with a new stream, " +
|
||||||
|
" when a new stream is available",
|
||||||
|
function(done) {
|
||||||
|
var endedStream = {ended: true};
|
||||||
|
var newStream = {};
|
||||||
|
multiplexGum.userMedia.localStream = endedStream;
|
||||||
|
navigator.originalGum.callsArgWith(1, newStream);
|
||||||
|
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, function onSuccess(localStream) {
|
||||||
|
expect(localStream).to.eql(newStream);
|
||||||
|
done();
|
||||||
|
}, null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#reset", function () {
|
||||||
|
it("should reset all userMedia state to default", function() {
|
||||||
|
// If userMedia is defined, then it needs to have all of
|
||||||
|
// the properties that multipleGum will depend on. It is
|
||||||
|
// easier to simply delete the object than to setup a fake
|
||||||
|
// state of the object.
|
||||||
|
delete multiplexGum.userMedia;
|
||||||
|
|
||||||
|
multiplexGum.reset();
|
||||||
|
|
||||||
|
expect(multiplexGum.userMedia).to.deep.equal({
|
||||||
|
error: null,
|
||||||
|
localStream: null,
|
||||||
|
pending: false,
|
||||||
|
errorCallbacks: [],
|
||||||
|
successCallbacks: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call all queued error callbacks with 'PERMISSION_DENIED'",
|
||||||
|
function(done) {
|
||||||
|
sandbox.stub(navigator, "originalGum");
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, function(localStream) {
|
||||||
|
sinon.assert.fail(
|
||||||
|
"The success callback shouldn't be called due to reset");
|
||||||
|
}, function(error) {
|
||||||
|
expect(error).to.equal("PERMISSION_DENIED");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
multiplexGum.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call MST.stop() on the stream tracks", function() {
|
||||||
|
var stopStub = sandbox.stub();
|
||||||
|
multiplexGum.userMedia.localStream = {stop: stopStub};
|
||||||
|
|
||||||
|
multiplexGum.reset();
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(stopStub);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not call MST.stop() on the stream tracks if .stop() doesn't exist",
|
||||||
|
function() {
|
||||||
|
multiplexGum.userMedia.localStream = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
multiplexGum.reset();
|
||||||
|
} catch (ex) {
|
||||||
|
sinon.assert.fail(
|
||||||
|
"reset shouldn't throw when a stream doesn't implement stop(): "
|
||||||
|
+ ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not get stuck in recursion if the error callback calls 'reset'",
|
||||||
|
function() {
|
||||||
|
sandbox.stub(navigator, "originalGum");
|
||||||
|
navigator.originalGum.callsArgWith(2, "PERMISSION_DENIED");
|
||||||
|
|
||||||
|
var calledOnce = false;
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, null, function() {
|
||||||
|
if (calledOnce) {
|
||||||
|
sinon.assert.fail("reset should only be called once");
|
||||||
|
}
|
||||||
|
calledOnce = true;
|
||||||
|
multiplexGum.reset.bind(multiplexGum)();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not get stuck in recursion if the success callback calls 'reset'",
|
||||||
|
function() {
|
||||||
|
sandbox.stub(navigator, "originalGum");
|
||||||
|
navigator.originalGum.callsArgWith(1, {});
|
||||||
|
|
||||||
|
var calledOnce = false;
|
||||||
|
multiplexGum.getPermsAndCacheMedia(null, function() {
|
||||||
|
calledOnce = true;
|
||||||
|
multiplexGum.reset.bind(multiplexGum)();
|
||||||
|
}, function() {
|
||||||
|
if (calledOnce) {
|
||||||
|
sinon.assert.fail("reset should only be called once");
|
||||||
|
}
|
||||||
|
calledOnce = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -13,9 +13,11 @@ describe("loop.webapp", function() {
|
|||||||
var sharedModels = loop.shared.models,
|
var sharedModels = loop.shared.models,
|
||||||
sharedViews = loop.shared.views,
|
sharedViews = loop.shared.views,
|
||||||
sharedUtils = loop.shared.utils,
|
sharedUtils = loop.shared.utils,
|
||||||
|
standaloneMedia = loop.standaloneMedia,
|
||||||
sandbox,
|
sandbox,
|
||||||
notifications,
|
notifications,
|
||||||
feedbackApiClient;
|
feedbackApiClient,
|
||||||
|
stubGetPermsAndCacheMedia;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
@ -23,6 +25,9 @@ describe("loop.webapp", function() {
|
|||||||
feedbackApiClient = new loop.FeedbackAPIClient("http://invalid", {
|
feedbackApiClient = new loop.FeedbackAPIClient("http://invalid", {
|
||||||
product: "Loop"
|
product: "Loop"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
stubGetPermsAndCacheMedia = sandbox.stub(
|
||||||
|
loop.standaloneMedia._MultiplexGum.prototype, "getPermsAndCacheMedia");
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
@ -610,6 +615,19 @@ describe("loop.webapp", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("HomeView", function() {
|
||||||
|
it("should call loop.standaloneMedia.reset", function() {
|
||||||
|
var multiplexGum = new standaloneMedia._MultiplexGum();
|
||||||
|
standaloneMedia.setSingleton(multiplexGum);
|
||||||
|
sandbox.stub(standaloneMedia._MultiplexGum.prototype, "reset");
|
||||||
|
|
||||||
|
TestUtils.renderIntoDocument(loop.webapp.HomeView());
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(multiplexGum.reset);
|
||||||
|
sinon.assert.calledWithExactly(multiplexGum.reset);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("PendingConversationView", function() {
|
describe("PendingConversationView", function() {
|
||||||
var view, websocket, fakeAudio;
|
var view, websocket, fakeAudio;
|
||||||
|
|
||||||
@ -652,6 +670,18 @@ describe("loop.webapp", function() {
|
|||||||
|
|
||||||
sinon.assert.calledOnce(websocket.cancel);
|
sinon.assert.calledOnce(websocket.cancel);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should call multiplexGum.reset to release the camera", function() {
|
||||||
|
var multiplexGum = new standaloneMedia._MultiplexGum();
|
||||||
|
standaloneMedia.setSingleton(multiplexGum);
|
||||||
|
sandbox.stub(standaloneMedia._MultiplexGum.prototype, "reset");
|
||||||
|
|
||||||
|
var button = view.getDOMNode().querySelector(".btn-cancel");
|
||||||
|
React.addons.TestUtils.Simulate.click(button);
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(multiplexGum.reset);
|
||||||
|
sinon.assert.calledWithExactly(multiplexGum.reset);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Events", function() {
|
describe("Events", function() {
|
||||||
@ -697,8 +727,27 @@ describe("loop.webapp", function() {
|
|||||||
client: standaloneClientStub
|
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",
|
it("should start the audio-video conversation establishment process",
|
||||||
function() {
|
function() {
|
||||||
var setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
|
var setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
|
||||||
@ -1002,6 +1051,9 @@ describe("loop.webapp", function() {
|
|||||||
client: standaloneClientStub
|
client: standaloneClientStub
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// default to succeeding with a null local media object
|
||||||
|
stubGetPermsAndCacheMedia.callsArgWith(1, {});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should start the conversation establishment process", function() {
|
it("should start the conversation establishment process", function() {
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
window.OTProperties.configURL = window.OTProperties.assetURL + 'js/dynamic_config.min.js';
|
window.OTProperties.configURL = window.OTProperties.assetURL + 'js/dynamic_config.min.js';
|
||||||
window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css';
|
window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css';
|
||||||
</script>
|
</script>
|
||||||
|
<script src="../content/js/multiplexGum.js"></script>
|
||||||
<script src="../content/shared/libs/sdk.js"></script>
|
<script src="../content/shared/libs/sdk.js"></script>
|
||||||
<script src="../content/shared/libs/react-0.11.2.js"></script>
|
<script src="../content/shared/libs/react-0.11.2.js"></script>
|
||||||
<script src="../content/shared/libs/jquery-2.1.0.js"></script>
|
<script src="../content/shared/libs/jquery-2.1.0.js"></script>
|
||||||
|
Loading…
Reference in New Issue
Block a user