зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1065201: introduce new sounds for Hello standalone and desktop. r=mikedeboer
This commit is contained in:
Родитель
61eeb953f5
Коммит
002e180974
|
@ -1650,7 +1650,7 @@ pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
|
|||
pref("loop.legal.ToS_url", "https://hello.firefox.com/legal/terms/");
|
||||
pref("loop.legal.privacy_url", "https://www.mozilla.org/privacy/");
|
||||
pref("loop.do_not_disturb", false);
|
||||
pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/Firefox-Long.ogg");
|
||||
pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/ringtone.ogg");
|
||||
pref("loop.retry_delay.start", 60000);
|
||||
pref("loop.retry_delay.limit", 300000);
|
||||
pref("loop.feedback.baseUrl", "https://input.mozilla.org/api/v1/feedback");
|
||||
|
@ -1660,9 +1660,9 @@ pref("loop.debug.dispatcher", false);
|
|||
pref("loop.debug.websocket", false);
|
||||
pref("loop.debug.sdk", false);
|
||||
#ifdef DEBUG
|
||||
pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*");
|
||||
pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*; media-src blob:");
|
||||
#else
|
||||
pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net");
|
||||
pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src 'self' data: http://www.gravatar.com/ about: file: chrome:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net; media-src blob:");
|
||||
#endif
|
||||
pref("loop.oauth.google.redirect_uri", "urn:ietf:wg:oauth:2.0:oob:auto");
|
||||
pref("loop.oauth.google.scope", "https://www.google.com/m8/feeds");
|
||||
|
|
|
@ -13,6 +13,7 @@ Cu.import("resource:///modules/loop/LoopCalls.jsm");
|
|||
Cu.import("resource:///modules/loop/MozLoopService.jsm");
|
||||
Cu.import("resource:///modules/loop/LoopRooms.jsm");
|
||||
Cu.import("resource:///modules/loop/LoopContacts.jsm");
|
||||
Cu.importGlobalProperties(["Blob"]);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopContacts",
|
||||
"resource:///modules/loop/LoopContacts.jsm");
|
||||
|
@ -685,6 +686,31 @@ function injectLoopAPI(targetWindow) {
|
|||
return MozLoopService.generateUUID();
|
||||
}
|
||||
},
|
||||
|
||||
getAudioBlob: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(name, callback) {
|
||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
let url = `chrome://browser/content/loop/shared/sounds/${name}.ogg`;
|
||||
|
||||
request.open("GET", url, true);
|
||||
request.responseType = "arraybuffer";
|
||||
request.onload = () => {
|
||||
if (request.status < 200 || request.status >= 300) {
|
||||
let error = new Error(request.status + " " + request.statusText);
|
||||
callback(cloneValueInto(error, targetWindow));
|
||||
return;
|
||||
}
|
||||
|
||||
let blob = new Blob([request.response], {type: "audio/ogg"});
|
||||
callback(null, cloneValueInto(blob, targetWindow));
|
||||
};
|
||||
|
||||
request.send();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function onStatusChanged(aSubject, aTopic, aData) {
|
||||
|
|
|
@ -21,7 +21,7 @@ loop.conversation = (function(mozL10n) {
|
|||
var DesktopRoomView = loop.roomViews.DesktopRoomView;
|
||||
|
||||
var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
|
||||
mixins: [sharedMixins.DropdownMenuMixin],
|
||||
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
model: React.PropTypes.object.isRequired,
|
||||
|
@ -185,10 +185,16 @@ loop.conversation = (function(mozL10n) {
|
|||
* incoming call views (bug 1088672).
|
||||
*/
|
||||
var GenericFailureView = React.createClass({displayName: 'GenericFailureView',
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
cancelCall: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("failure");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
document.title = mozL10n.get("generic_failure_title");
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ loop.conversation = (function(mozL10n) {
|
|||
var DesktopRoomView = loop.roomViews.DesktopRoomView;
|
||||
|
||||
var IncomingCallView = React.createClass({
|
||||
mixins: [sharedMixins.DropdownMenuMixin],
|
||||
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
model: React.PropTypes.object.isRequired,
|
||||
|
@ -185,10 +185,16 @@ loop.conversation = (function(mozL10n) {
|
|||
* incoming call views (bug 1088672).
|
||||
*/
|
||||
var GenericFailureView = React.createClass({
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
cancelCall: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("failure");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
document.title = mozL10n.get("generic_failure_title");
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
var sharedActions = loop.shared.actions;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
|
||||
// This duplicates a similar function in contacts.jsx that isn't used in the
|
||||
// conversation window. If we get too many of these, we might want to consider
|
||||
|
@ -133,6 +134,8 @@ loop.conversationViews = (function(mozL10n) {
|
|||
* pending/ringing strings.
|
||||
*/
|
||||
var PendingConversationView = React.createClass({displayName: 'PendingConversationView',
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
callState: React.PropTypes.string,
|
||||
|
@ -146,6 +149,10 @@ loop.conversationViews = (function(mozL10n) {
|
|||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("ringtone", {loop: true});
|
||||
},
|
||||
|
||||
cancelCall: function() {
|
||||
this.props.dispatcher.dispatch(new sharedActions.CancelCall());
|
||||
},
|
||||
|
@ -186,7 +193,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
* Call failed view. Displayed when a call fails.
|
||||
*/
|
||||
var CallFailedView = React.createClass({displayName: 'CallFailedView',
|
||||
mixins: [Backbone.Events],
|
||||
mixins: [Backbone.Events, sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
|
@ -205,6 +212,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("failure");
|
||||
this.listenTo(this.props.store, "change:emailLink",
|
||||
this._onEmailLinkReceived);
|
||||
this.listenTo(this.props.store, "error:emailLink",
|
||||
|
|
|
@ -14,6 +14,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
var sharedActions = loop.shared.actions;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
|
||||
// This duplicates a similar function in contacts.jsx that isn't used in the
|
||||
// conversation window. If we get too many of these, we might want to consider
|
||||
|
@ -133,6 +134,8 @@ loop.conversationViews = (function(mozL10n) {
|
|||
* pending/ringing strings.
|
||||
*/
|
||||
var PendingConversationView = React.createClass({
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
callState: React.PropTypes.string,
|
||||
|
@ -146,6 +149,10 @@ loop.conversationViews = (function(mozL10n) {
|
|||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("ringtone", {loop: true});
|
||||
},
|
||||
|
||||
cancelCall: function() {
|
||||
this.props.dispatcher.dispatch(new sharedActions.CancelCall());
|
||||
},
|
||||
|
@ -186,7 +193,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
* Call failed view. Displayed when a call fails.
|
||||
*/
|
||||
var CallFailedView = React.createClass({
|
||||
mixins: [Backbone.Events],
|
||||
mixins: [Backbone.Events, sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
|
@ -205,6 +212,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("failure");
|
||||
this.listenTo(this.props.store, "change:emailLink",
|
||||
this._onEmailLinkReceived);
|
||||
this.listenTo(this.props.store, "error:emailLink",
|
||||
|
|
|
@ -141,6 +141,7 @@ loop.shared.mixins = (function() {
|
|||
*/
|
||||
var AudioMixin = {
|
||||
audio: null,
|
||||
_audioRequest: null,
|
||||
|
||||
_isLoopDesktop: function() {
|
||||
return typeof rootObject.navigator.mozLoop === "object";
|
||||
|
@ -149,27 +150,62 @@ loop.shared.mixins = (function() {
|
|||
/**
|
||||
* Starts playing an audio file, stopping any audio that is already in progress.
|
||||
*
|
||||
* @param {String} filename The filename to play (excluding the extension).
|
||||
* @param {String} name The filename to play (excluding the extension).
|
||||
*/
|
||||
play: function(filename, options) {
|
||||
if (this._isLoopDesktop()) {
|
||||
// XXX: We need navigator.mozLoop.playSound(name), see Bug 1089585.
|
||||
return;
|
||||
}
|
||||
|
||||
play: function(name, options) {
|
||||
options = options || {};
|
||||
options.loop = options.loop || false;
|
||||
|
||||
this._ensureAudioStopped();
|
||||
this.audio = new Audio('shared/sounds/' + filename + ".ogg");
|
||||
this._getAudioBlob(name, function(error, blob) {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
var url = URL.createObjectURL(blob);
|
||||
this.audio = new Audio(url);
|
||||
this.audio.loop = options.loop;
|
||||
this.audio.play();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_getAudioBlob: function(name, callback) {
|
||||
if (this._isLoopDesktop()) {
|
||||
rootObject.navigator.mozLoop.getAudioBlob(name, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
var url = "shared/sounds/" + name + ".ogg";
|
||||
this._audioRequest = new XMLHttpRequest();
|
||||
this._audioRequest.open("GET", url, true);
|
||||
this._audioRequest.responseType = "arraybuffer";
|
||||
this._audioRequest.onload = function() {
|
||||
var request = this._audioRequest;
|
||||
var error;
|
||||
if (request.status < 200 || request.status >= 300) {
|
||||
error = new Error(request.status + " " + request.statusText);
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
var type = request.getResponseHeader("Content-Type");
|
||||
var blob = new Blob([request.response], {type: type});
|
||||
callback(null, blob);
|
||||
}.bind(this);
|
||||
|
||||
this._audioRequest.send(null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures audio is stopped playing, and removes the object from memory.
|
||||
*/
|
||||
_ensureAudioStopped: function() {
|
||||
if (this._audioRequest) {
|
||||
this._audioRequest.abort();
|
||||
delete this._audioRequest;
|
||||
}
|
||||
|
||||
if (this.audio) {
|
||||
this.audio.pause();
|
||||
this.audio.removeAttribute("src");
|
||||
|
|
|
@ -540,6 +540,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||
* Feedback view.
|
||||
*/
|
||||
var FeedbackView = React.createClass({displayName: 'FeedbackView',
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
// A loop.FeedbackAPIClient instance
|
||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
||||
|
@ -556,6 +558,10 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||
return {step: "start"};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("terminated");
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.setState(this.getInitialState());
|
||||
},
|
||||
|
|
|
@ -540,6 +540,8 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||
* Feedback view.
|
||||
*/
|
||||
var FeedbackView = React.createClass({
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
// A loop.FeedbackAPIClient instance
|
||||
feedbackApiClient: React.PropTypes.object.isRequired,
|
||||
|
@ -556,6 +558,10 @@ loop.shared.views = (function(_, OT, l10n) {
|
|||
return {step: "start"};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("terminated");
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.setState(this.getInitialState());
|
||||
},
|
||||
|
|
Двоичные данные
browser/components/loop/content/shared/sounds/Firefox-Long.ogg
Двоичные данные
browser/components/loop/content/shared/sounds/Firefox-Long.ogg
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -81,7 +81,11 @@ browser.jar:
|
|||
content/browser/loop/shared/libs/backbone-1.1.2.js (content/shared/libs/backbone-1.1.2.js)
|
||||
|
||||
# Shared sounds
|
||||
content/browser/loop/shared/sounds/Firefox-Long.ogg (content/shared/sounds/Firefox-Long.ogg)
|
||||
content/browser/loop/shared/sounds/ringtone.ogg (content/shared/sounds/ringtone.ogg)
|
||||
content/browser/loop/shared/sounds/connecting.ogg (content/shared/sounds/connecting.ogg)
|
||||
content/browser/loop/shared/sounds/connected.ogg (content/shared/sounds/connected.ogg)
|
||||
content/browser/loop/shared/sounds/terminated.ogg (content/shared/sounds/terminated.ogg)
|
||||
content/browser/loop/shared/sounds/failure.ogg (content/shared/sounds/failure.ogg)
|
||||
|
||||
# Partner SDK assets
|
||||
content/browser/loop/libs/sdk.js (content/shared/libs/sdk.js)
|
||||
|
|
|
@ -286,7 +286,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||
},
|
||||
|
||||
_handleRingingProgress: function() {
|
||||
this.play("ringing", {loop: true});
|
||||
this.play("ringtone", {loop: true});
|
||||
this.setState({callState: "ringing"});
|
||||
},
|
||||
|
||||
|
@ -534,8 +534,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||
* Ended conversation view.
|
||||
*/
|
||||
var EndedConversationView = React.createClass({displayName: 'EndedConversationView',
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
|
@ -544,10 +542,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||
onAfterFeedbackReceived: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("terminated");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
document.title = mozL10n.get("standalone_title_with_status",
|
||||
{clientShortname: mozL10n.get("clientShortname2"),
|
||||
|
|
|
@ -286,7 +286,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||
},
|
||||
|
||||
_handleRingingProgress: function() {
|
||||
this.play("ringing", {loop: true});
|
||||
this.play("ringtone", {loop: true});
|
||||
this.setState({callState: "ringing"});
|
||||
},
|
||||
|
||||
|
@ -534,8 +534,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||
* Ended conversation view.
|
||||
*/
|
||||
var EndedConversationView = React.createClass({
|
||||
mixins: [sharedMixins.AudioMixin],
|
||||
|
||||
propTypes: {
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
|
@ -544,10 +542,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
|||
onAfterFeedbackReceived: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.play("terminated");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
document.title = mozL10n.get("standalone_title_with_status",
|
||||
{clientShortname: mozL10n.get("clientShortname2"),
|
||||
|
|
|
@ -7,7 +7,7 @@ describe("loop.conversationViews", function () {
|
|||
"use strict";
|
||||
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var sandbox, oldTitle, view, dispatcher, contact;
|
||||
var sandbox, oldTitle, view, dispatcher, contact, fakeAudioXHR;
|
||||
|
||||
var CALL_STATES = loop.store.CALL_STATES;
|
||||
|
||||
|
@ -30,11 +30,39 @@ describe("loop.conversationViews", function () {
|
|||
pref: true
|
||||
}]
|
||||
};
|
||||
fakeAudioXHR = {
|
||||
open: sinon.spy(),
|
||||
send: function() {},
|
||||
abort: function() {},
|
||||
getResponseHeader: function(header) {
|
||||
if (header === "Content-Type")
|
||||
return "audio/ogg";
|
||||
},
|
||||
responseType: null,
|
||||
response: new ArrayBuffer(10),
|
||||
onload: null
|
||||
};
|
||||
|
||||
navigator.mozLoop = {
|
||||
getLoopCharPref: sinon.stub().returns("http://fakeurl"),
|
||||
composeEmail: sinon.spy(),
|
||||
get appVersionInfo() {
|
||||
return {
|
||||
version: "42",
|
||||
channel: "test",
|
||||
platform: "test"
|
||||
};
|
||||
},
|
||||
getAudioBlob: sinon.spy(function(name, callback) {
|
||||
callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
document.title = oldTitle;
|
||||
view = undefined;
|
||||
delete navigator.mozLoop;
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
|
@ -202,7 +230,7 @@ describe("loop.conversationViews", function () {
|
|||
});
|
||||
|
||||
describe("CallFailedView", function() {
|
||||
var store;
|
||||
var store, fakeAudio;
|
||||
|
||||
function mountTestComponent(props) {
|
||||
return TestUtils.renderIntoDocument(
|
||||
|
@ -219,6 +247,12 @@ describe("loop.conversationViews", function () {
|
|||
client: {},
|
||||
sdkDriver: {}
|
||||
});
|
||||
fakeAudio = {
|
||||
play: sinon.spy(),
|
||||
pause: sinon.spy(),
|
||||
removeAttribute: sinon.spy()
|
||||
};
|
||||
sandbox.stub(window, "Audio").returns(fakeAudio);
|
||||
});
|
||||
|
||||
it("should dispatch a retryCall action when the retry button is pressed",
|
||||
|
@ -306,6 +340,16 @@ describe("loop.conversationViews", function () {
|
|||
|
||||
expect(view.getDOMNode().querySelector(".btn-email").disabled).eql(false);
|
||||
});
|
||||
|
||||
it("should play a failure sound, once", function() {
|
||||
view = mountTestComponent();
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.getAudioBlob);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob,
|
||||
"failure", sinon.match.func);
|
||||
sinon.assert.calledOnce(fakeAudio.play);
|
||||
expect(fakeAudio.loop).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("OngoingConversationView", function() {
|
||||
|
@ -412,11 +456,6 @@ describe("loop.conversationViews", function () {
|
|||
}
|
||||
|
||||
beforeEach(function() {
|
||||
navigator.mozLoop = {
|
||||
getLoopCharPref: function() { return "fake"; },
|
||||
appVersionInfo: sinon.spy()
|
||||
};
|
||||
|
||||
store = new loop.store.ConversationStore({}, {
|
||||
dispatcher: dispatcher,
|
||||
client: {},
|
||||
|
@ -424,10 +463,6 @@ describe("loop.conversationViews", function () {
|
|||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
delete navigator.mozLoop;
|
||||
});
|
||||
|
||||
it("should render the CallFailedView when the call state is 'terminated'",
|
||||
function() {
|
||||
store.set({callState: CALL_STATES.TERMINATED});
|
||||
|
|
|
@ -57,7 +57,10 @@ describe("loop.conversation", function() {
|
|||
channel: "test",
|
||||
platform: "test"
|
||||
};
|
||||
}
|
||||
},
|
||||
getAudioBlob: sinon.spy(function(name, callback) {
|
||||
callback(null, new Blob([new ArrayBuffer(10)], {type: 'audio/ogg'}));
|
||||
})
|
||||
};
|
||||
|
||||
// XXX These stubs should be hoisted in a common file
|
||||
|
@ -747,7 +750,7 @@ describe("loop.conversation", function() {
|
|||
});
|
||||
|
||||
describe("IncomingCallView", function() {
|
||||
var view, model;
|
||||
var view, model, fakeAudio;
|
||||
|
||||
beforeEach(function() {
|
||||
var Model = Backbone.Model.extend({
|
||||
|
@ -757,6 +760,13 @@ describe("loop.conversation", function() {
|
|||
sandbox.spy(model, "trigger");
|
||||
sandbox.stub(model, "set");
|
||||
|
||||
fakeAudio = {
|
||||
play: sinon.spy(),
|
||||
pause: sinon.spy(),
|
||||
removeAttribute: sinon.spy()
|
||||
};
|
||||
sandbox.stub(window, "Audio").returns(fakeAudio);
|
||||
|
||||
view = TestUtils.renderIntoDocument(loop.conversation.IncomingCallView({
|
||||
model: model,
|
||||
video: true
|
||||
|
@ -896,4 +906,32 @@ describe("loop.conversation", function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("GenericFailureView", function() {
|
||||
var view, fakeAudio;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeAudio = {
|
||||
play: sinon.spy(),
|
||||
pause: sinon.spy(),
|
||||
removeAttribute: sinon.spy()
|
||||
};
|
||||
sandbox.stub(window, "Audio").returns(fakeAudio);
|
||||
|
||||
view = TestUtils.renderIntoDocument(
|
||||
loop.conversation.GenericFailureView({
|
||||
cancelCall: function() {}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should play a failure sound, once", function() {
|
||||
sinon.assert.calledOnce(navigator.mozLoop.getAudioBlob);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob,
|
||||
"failure", sinon.match.func);
|
||||
sinon.assert.calledOnce(fakeAudio.play);
|
||||
expect(fakeAudio.loop).to.equal(false);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,7 +15,7 @@ describe("loop.shared.views", function() {
|
|||
var sharedModels = loop.shared.models,
|
||||
sharedViews = loop.shared.views,
|
||||
getReactElementByClass = TestUtils.findRenderedDOMComponentWithClass,
|
||||
sandbox;
|
||||
sandbox, fakeAudioXHR;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
@ -23,6 +23,18 @@ describe("loop.shared.views", function() {
|
|||
sandbox.stub(l10n, "get", function(x) {
|
||||
return "translated:" + x;
|
||||
});
|
||||
fakeAudioXHR = {
|
||||
open: sinon.spy(),
|
||||
send: function() {},
|
||||
abort: function() {},
|
||||
getResponseHeader: function(header) {
|
||||
if (header === "Content-Type")
|
||||
return "audio/ogg";
|
||||
},
|
||||
responseType: null,
|
||||
response: new ArrayBuffer(10),
|
||||
onload: null
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
|
@ -368,16 +380,55 @@ describe("loop.shared.views", function() {
|
|||
|
||||
it("should play a connected sound, once, on session:connected",
|
||||
function() {
|
||||
var url = "shared/sounds/connected.ogg";
|
||||
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
||||
model.trigger("session:connected");
|
||||
|
||||
sinon.assert.calledOnce(window.Audio);
|
||||
sinon.assert.calledWithExactly(
|
||||
window.Audio, "shared/sounds/connected.ogg");
|
||||
fakeAudioXHR.onload();
|
||||
|
||||
sinon.assert.called(fakeAudioXHR.open);
|
||||
sinon.assert.calledWithExactly(fakeAudioXHR.open, "GET", url, true);
|
||||
|
||||
sinon.assert.calledOnce(fakeAudio.play);
|
||||
expect(fakeAudio.loop).to.not.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("for desktop", function() {
|
||||
var origMozLoop;
|
||||
|
||||
beforeEach(function() {
|
||||
origMozLoop = navigator.mozLoop;
|
||||
navigator.mozLoop = {
|
||||
getAudioBlob: sinon.spy(function(name, callback) {
|
||||
var data = new ArrayBuffer(10);
|
||||
callback(null, new Blob([data], {type: "audio/ogg"}));
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
navigator.mozLoop = origMozLoop;
|
||||
});
|
||||
|
||||
it("should play a connected sound, once, on session:connected",
|
||||
function() {
|
||||
var url = "chrome://browser/content/loop/shared/sounds/connected.ogg";
|
||||
model.trigger("session:connected");
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.getAudioBlob);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob,
|
||||
"connected", sinon.match.func);
|
||||
sinon.assert.calledOnce(fakeAudio.play);
|
||||
expect(fakeAudio.loop).to.not.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("for both (standalone and desktop)", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
||||
});
|
||||
|
||||
it("should start streaming on session:connected", function() {
|
||||
model.trigger("session:connected");
|
||||
|
||||
|
@ -458,6 +509,7 @@ describe("loop.shared.views", function() {
|
|||
|
||||
beforeEach(function() {
|
||||
fakeFeedbackApiClient = {send: sandbox.stub()};
|
||||
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
||||
comp = TestUtils.renderIntoDocument(sharedViews.FeedbackView({
|
||||
feedbackApiClient: fakeFeedbackApiClient
|
||||
}));
|
||||
|
|
|
@ -18,7 +18,8 @@ describe("loop.webapp", function() {
|
|||
sandbox,
|
||||
notifications,
|
||||
feedbackApiClient,
|
||||
stubGetPermsAndCacheMedia;
|
||||
stubGetPermsAndCacheMedia,
|
||||
fakeAudioXHR;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
@ -29,6 +30,19 @@ describe("loop.webapp", function() {
|
|||
|
||||
stubGetPermsAndCacheMedia = sandbox.stub(
|
||||
loop.standaloneMedia._MultiplexGum.prototype, "getPermsAndCacheMedia");
|
||||
|
||||
fakeAudioXHR = {
|
||||
open: sinon.spy(),
|
||||
send: function() {},
|
||||
abort: function() {},
|
||||
getResponseHeader: function(header) {
|
||||
if (header === "Content-Type")
|
||||
return "audio/ogg";
|
||||
},
|
||||
responseType: null,
|
||||
response: new ArrayBuffer(10),
|
||||
onload: null
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
|
@ -219,6 +233,7 @@ describe("loop.webapp", function() {
|
|||
describe("state: terminate, reason: reject", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(notifications, "errorL10n");
|
||||
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
||||
});
|
||||
|
||||
it("should display the FailedConversationView", function() {
|
||||
|
@ -307,6 +322,7 @@ describe("loop.webapp", function() {
|
|||
promiseConnectStub =
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect");
|
||||
promiseConnectStub.returns(new Promise(function(resolve, reject) {}));
|
||||
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
||||
});
|
||||
|
||||
describe("call:outgoing", function() {
|
||||
|
@ -526,6 +542,8 @@ describe("loop.webapp", function() {
|
|||
var view, conversation, client, fakeAudio;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
||||
|
||||
fakeAudio = {
|
||||
play: sinon.spy(),
|
||||
pause: sinon.spy(),
|
||||
|
@ -541,6 +559,7 @@ describe("loop.webapp", function() {
|
|||
});
|
||||
conversation.set("loopToken", "fakeToken");
|
||||
|
||||
sandbox.stub(client, "requestCallUrlInfo");
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.FailedConversationView({
|
||||
conversation: conversation,
|
||||
|
@ -550,9 +569,12 @@ describe("loop.webapp", function() {
|
|||
});
|
||||
|
||||
it("should play a failure sound, once", function() {
|
||||
sinon.assert.calledOnce(window.Audio);
|
||||
sinon.assert.calledWithExactly(window.Audio,
|
||||
"shared/sounds/failure.ogg");
|
||||
fakeAudioXHR.onload();
|
||||
|
||||
sinon.assert.called(fakeAudioXHR.open);
|
||||
sinon.assert.calledWithExactly(
|
||||
fakeAudioXHR.open, "GET", "shared/sounds/failure.ogg", true);
|
||||
sinon.assert.calledOnce(fakeAudio.play);
|
||||
expect(fakeAudio.loop).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
@ -678,6 +700,7 @@ describe("loop.webapp", function() {
|
|||
removeAttribute: sinon.spy()
|
||||
};
|
||||
sandbox.stub(window, "Audio").returns(fakeAudio);
|
||||
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
||||
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.PendingConversationView({
|
||||
|
@ -689,8 +712,12 @@ describe("loop.webapp", function() {
|
|||
describe("#componentDidMount", function() {
|
||||
|
||||
it("should play a looped connecting sound", function() {
|
||||
sinon.assert.calledOnce(window.Audio);
|
||||
sinon.assert.calledWithExactly(window.Audio, "shared/sounds/connecting.ogg");
|
||||
fakeAudioXHR.onload();
|
||||
|
||||
sinon.assert.called(fakeAudioXHR.open);
|
||||
sinon.assert.calledWithExactly(
|
||||
fakeAudioXHR.open, "GET", "shared/sounds/connecting.ogg", true);
|
||||
sinon.assert.calledOnce(fakeAudio.play);
|
||||
expect(fakeAudio.loop).to.equal(true);
|
||||
});
|
||||
|
||||
|
@ -727,8 +754,13 @@ describe("loop.webapp", function() {
|
|||
|
||||
it("should play a looped ringing sound", function() {
|
||||
websocket.trigger("progress:alerting");
|
||||
fakeAudioXHR.onload();
|
||||
|
||||
sinon.assert.calledWithExactly(window.Audio, "shared/sounds/ringing.ogg");
|
||||
sinon.assert.called(fakeAudioXHR.open);
|
||||
sinon.assert.calledWithExactly(
|
||||
fakeAudioXHR.open, "GET", "shared/sounds/ringtone.ogg", true);
|
||||
|
||||
sinon.assert.called(fakeAudio.play);
|
||||
expect(fakeAudio.loop).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
@ -997,6 +1029,7 @@ describe("loop.webapp", function() {
|
|||
conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: {}
|
||||
});
|
||||
sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.EndedConversationView({
|
||||
conversation: conversation,
|
||||
|
@ -1018,8 +1051,13 @@ describe("loop.webapp", function() {
|
|||
describe("#componentDidMount", function() {
|
||||
|
||||
it("should play a terminating sound, once", function() {
|
||||
sinon.assert.calledOnce(window.Audio);
|
||||
sinon.assert.calledWithExactly(window.Audio, "shared/sounds/terminated.ogg");
|
||||
fakeAudioXHR.onload();
|
||||
|
||||
sinon.assert.called(fakeAudioXHR.open);
|
||||
sinon.assert.calledWithExactly(
|
||||
fakeAudioXHR.open, "GET", "shared/sounds/terminated.ogg", true);
|
||||
|
||||
sinon.assert.calledOnce(fakeAudio.play);
|
||||
expect(fakeAudio.loop).to.not.equal(true);
|
||||
});
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче