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.cssURL = window.OTProperties.assetURL + 'css/ot.css';
|
||||
</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="libs/l10n-gaia-02ca67948fe8.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 sharedUtils = loop.shared.utils;
|
||||
|
||||
var multiplexGum = loop.standaloneMedia.multiplexGum;
|
||||
|
||||
/**
|
||||
* Homepage view.
|
||||
*/
|
||||
var HomeView = React.createClass({displayName: 'HomeView',
|
||||
render: function() {
|
||||
loop.standaloneMedia.multiplexGum.reset();
|
||||
return (
|
||||
React.DOM.p(null, mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")}))
|
||||
);
|
||||
@ -287,6 +290,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
},
|
||||
|
||||
_cancelOutgoingCall: function() {
|
||||
loop.standaloneMedia.multiplexGum.reset();
|
||||
this.props.websocket.cancel();
|
||||
},
|
||||
|
||||
@ -448,8 +452,15 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*/
|
||||
startCall: function(callType) {
|
||||
return function() {
|
||||
this.props.conversation.setupOutgoingCall(callType);
|
||||
this.setState({disableCallButton: true});
|
||||
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);
|
||||
},
|
||||
|
||||
|
@ -19,11 +19,14 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
|
||||
var multiplexGum = loop.standaloneMedia.multiplexGum;
|
||||
|
||||
/**
|
||||
* Homepage view.
|
||||
*/
|
||||
var HomeView = React.createClass({
|
||||
render: function() {
|
||||
loop.standaloneMedia.multiplexGum.reset();
|
||||
return (
|
||||
<p>{mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")})}</p>
|
||||
);
|
||||
@ -287,6 +290,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
},
|
||||
|
||||
_cancelOutgoingCall: function() {
|
||||
loop.standaloneMedia.multiplexGum.reset();
|
||||
this.props.websocket.cancel();
|
||||
},
|
||||
|
||||
@ -448,8 +452,15 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*/
|
||||
startCall: function(callType) {
|
||||
return function() {
|
||||
this.props.conversation.setupOutgoingCall(callType);
|
||||
this.setState({disableCallButton: true});
|
||||
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);
|
||||
},
|
||||
|
||||
|
@ -37,11 +37,13 @@
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/websocket.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/webapp.js"></script>
|
||||
<!-- Test scripts -->
|
||||
<script src="standalone_client_test.js"></script>
|
||||
<script src="webapp_test.js"></script>
|
||||
<script src="multiplexGum_test.js"></script>
|
||||
<script>
|
||||
mocha.run(function () {
|
||||
$("#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,
|
||||
sharedViews = loop.shared.views,
|
||||
sharedUtils = loop.shared.utils,
|
||||
standaloneMedia = loop.standaloneMedia,
|
||||
sandbox,
|
||||
notifications,
|
||||
feedbackApiClient;
|
||||
feedbackApiClient,
|
||||
stubGetPermsAndCacheMedia;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@ -23,6 +25,9 @@ describe("loop.webapp", function() {
|
||||
feedbackApiClient = new loop.FeedbackAPIClient("http://invalid", {
|
||||
product: "Loop"
|
||||
});
|
||||
|
||||
stubGetPermsAndCacheMedia = sandbox.stub(
|
||||
loop.standaloneMedia._MultiplexGum.prototype, "getPermsAndCacheMedia");
|
||||
});
|
||||
|
||||
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() {
|
||||
var view, websocket, fakeAudio;
|
||||
|
||||
@ -652,6 +670,18 @@ describe("loop.webapp", function() {
|
||||
|
||||
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() {
|
||||
@ -697,8 +727,27 @@ 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",
|
||||
function() {
|
||||
var setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
|
||||
@ -1002,6 +1051,9 @@ describe("loop.webapp", function() {
|
||||
client: standaloneClientStub
|
||||
})
|
||||
);
|
||||
|
||||
// default to succeeding with a null local media object
|
||||
stubGetPermsAndCacheMedia.callsArgWith(1, {});
|
||||
});
|
||||
|
||||
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.cssURL = window.OTProperties.assetURL + 'css/ot.css';
|
||||
</script>
|
||||
<script src="../content/js/multiplexGum.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/jquery-2.1.0.js"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user