mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 08:15:31 +00:00
Bug 985596 Improve conversation model to handle incoming and outgoing calls. r=dmose
--HG-- extra : transplant_source : %DA%3FS%93%89%FF%90S3%E7%18%26%5D%2An.K%EA%BA%80
This commit is contained in:
parent
4ac3aa169a
commit
a87e3940b2
163
browser/components/loop/content/shared/js/client.js
Normal file
163
browser/components/loop/content/shared/js/client.js
Normal file
@ -0,0 +1,163 @@
|
||||
/* 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:true */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.shared = loop.shared || {};
|
||||
loop.shared.Client = (function($) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Loop server client.
|
||||
*
|
||||
* @param {Object} settings Settings object.
|
||||
*/
|
||||
function Client(settings) {
|
||||
settings = settings || {};
|
||||
if (!settings.hasOwnProperty("baseServerUrl") ||
|
||||
!settings.baseServerUrl) {
|
||||
throw new Error("missing required baseServerUrl");
|
||||
}
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
Client.prototype = {
|
||||
/**
|
||||
* Validates a data object to confirm it has the specified properties.
|
||||
*
|
||||
* @param {Object} The data object to verify
|
||||
* @param {Array} The list of properties to verify within the object
|
||||
* @return This returns either the specific property if only one
|
||||
* property is specified, or it returns all properties
|
||||
*/
|
||||
_validate: function(data, properties) {
|
||||
if (typeof data !== "object") {
|
||||
throw new Error("Invalid data received from server");
|
||||
}
|
||||
|
||||
properties.forEach(function (property) {
|
||||
if (!data.hasOwnProperty(property)) {
|
||||
throw new Error("Invalid data received from server - missing " +
|
||||
property);
|
||||
}
|
||||
});
|
||||
|
||||
if (properties.length <= 1) {
|
||||
return data[properties[0]];
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Generic handler for XHR failures.
|
||||
*
|
||||
* @param {Function} cb Callback(err)
|
||||
* @param jqXHR See jQuery docs
|
||||
* @param testStatus See jQuery docs
|
||||
* @param errorThrown See jQuery docs
|
||||
*/
|
||||
_failureHandler: function(cb, jqXHR, testStatus, errorThrown) {
|
||||
var error = "Unknown error.";
|
||||
if (jqXHR && jqXHR.responseJSON && jqXHR.responseJSON.error) {
|
||||
error = jqXHR.responseJSON.error;
|
||||
}
|
||||
var message = "HTTP error " + jqXHR.status + ": " +
|
||||
errorThrown + "; " + error;
|
||||
console.error(message);
|
||||
cb(new Error(message));
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests a call URL from the Loop server.
|
||||
*
|
||||
* @param {String} simplepushUrl a registered Simple Push URL
|
||||
* @param {string} nickname the nickname of the future caller
|
||||
* @param {Function} cb Callback(err, callUrl)
|
||||
*/
|
||||
requestCallUrl: function(nickname, cb) {
|
||||
var endpoint = this.settings.baseServerUrl + "/call-url/",
|
||||
reqData = {callerId: nickname};
|
||||
|
||||
var req = $.post(endpoint, reqData, function(callUrlData) {
|
||||
try {
|
||||
cb(null, this._validate(callUrlData, ["call_url"]));
|
||||
} catch (err) {
|
||||
console.log("Error requesting call info", err);
|
||||
cb(err);
|
||||
}
|
||||
}.bind(this), "json");
|
||||
|
||||
req.fail(this._failureHandler.bind(this, cb));
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests call information from the server for all calls since the
|
||||
* given version.
|
||||
*
|
||||
* @param {String} version the version identifier from the push
|
||||
* notification
|
||||
* @param {Function} cb Callback(err, calls)
|
||||
*/
|
||||
requestCallsInfo: function(version, cb) {
|
||||
if (!version) {
|
||||
throw new Error("missing required parameter version");
|
||||
}
|
||||
|
||||
var endpoint = this.settings.baseServerUrl + "/calls";
|
||||
|
||||
// XXX It is likely that we'll want to move some of this to whatever
|
||||
// opens the chat window, but we'll need to decide that once we make a
|
||||
// decision on chrome versus content, and know if we're going with
|
||||
// LoopService or a frameworker.
|
||||
var req = $.get(endpoint + "?version=" + version, function(callsData) {
|
||||
try {
|
||||
cb(null, this._validate(callsData, ["calls"]));
|
||||
} catch (err) {
|
||||
console.log("Error requesting calls info", err);
|
||||
cb(err);
|
||||
}
|
||||
}.bind(this), "json");
|
||||
|
||||
req.fail(this._failureHandler.bind(this, cb));
|
||||
},
|
||||
|
||||
/**
|
||||
* Posts a call request to the server for a call represented by the
|
||||
* loopToken. Will return the session data for the call.
|
||||
*
|
||||
* @param {String} loopToken The loopToken representing the call
|
||||
* @param {Function} cb Callback(err, sessionData)
|
||||
*/
|
||||
requestCallInfo: function(loopToken, cb) {
|
||||
if (!loopToken) {
|
||||
throw new Error("missing required parameter loopToken");
|
||||
}
|
||||
|
||||
var req = $.ajax({
|
||||
url: this.settings.baseServerUrl + "/calls/" + loopToken,
|
||||
method: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({}),
|
||||
dataType: "json"
|
||||
});
|
||||
|
||||
req.done(function(sessionData) {
|
||||
try {
|
||||
cb(null, this._validate(sessionData, [
|
||||
"sessionId", "sessionToken", "apiKey"
|
||||
]));
|
||||
} catch (err) {
|
||||
console.log("Error requesting call info", err);
|
||||
cb(err);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
req.fail(this._failureHandler.bind(this, cb));
|
||||
}
|
||||
};
|
||||
|
||||
return Client;
|
||||
})(jQuery);
|
@ -16,6 +16,10 @@ loop.shared.models = (function() {
|
||||
defaults: {
|
||||
callerId: undefined, // Loop caller id
|
||||
loopToken: undefined, // Loop conversation token
|
||||
loopVersion: undefined, // Loop version for /calls/ information. This
|
||||
// is the version received from the push
|
||||
// notification and is used by the server to
|
||||
// determine the pending calls
|
||||
sessionId: undefined, // TB session id
|
||||
sessionToken: undefined, // TB session token
|
||||
apiKey: undefined // TB api key
|
||||
@ -26,48 +30,56 @@ loop.shared.models = (function() {
|
||||
* server and updates appropriately the current model attributes with the
|
||||
* data.
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* - {String} baseServerUrl The server URL
|
||||
* - {Boolean} outgoing Set to true if this model represents the
|
||||
* outgoing call.
|
||||
*
|
||||
* Triggered events:
|
||||
*
|
||||
* - `session:ready` when the session information have been successfully
|
||||
* retrieved from the server;
|
||||
* - `session:error` when the request failed.
|
||||
*
|
||||
* @param {String} baseServerUrl The server URL
|
||||
* @throws {Error} If no baseServerUrl is given
|
||||
* @throws {Error} If no conversation token is set
|
||||
* @param {Object} options Options object
|
||||
*/
|
||||
initiate: function(baseServerUrl) {
|
||||
if (!baseServerUrl) {
|
||||
throw new Error("baseServerUrl arg must be passed to initiate()");
|
||||
}
|
||||
|
||||
if (!this.get("loopToken")) {
|
||||
throw new Error("missing required attribute loopToken");
|
||||
}
|
||||
|
||||
initiate: function(options) {
|
||||
// Check if the session is already set
|
||||
if (this.isSessionReady()) {
|
||||
return this.trigger("session:ready", this);
|
||||
}
|
||||
|
||||
var request = $.ajax({
|
||||
url: baseServerUrl + "/calls/" + this.get("loopToken"),
|
||||
method: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({}),
|
||||
dataType: "json"
|
||||
var client = new loop.shared.Client({
|
||||
baseServerUrl: options.baseServerUrl
|
||||
});
|
||||
|
||||
request.done(this.setReady.bind(this));
|
||||
|
||||
request.fail(function(xhr, _, statusText) {
|
||||
var serverError = xhr.status + " " + statusText;
|
||||
if (typeof xhr.responseJSON === "object" && xhr.responseJSON.error) {
|
||||
serverError += "; " + xhr.responseJSON.error;
|
||||
function handleResult(err, sessionData) {
|
||||
/*jshint validthis:true */
|
||||
if (err) {
|
||||
this.trigger("session:error", new Error(
|
||||
"Retrieval of session information failed: HTTP " + err));
|
||||
return;
|
||||
}
|
||||
this.trigger("session:error", new Error(
|
||||
"Retrieval of session information failed: HTTP " + serverError));
|
||||
}.bind(this));
|
||||
|
||||
// XXX For incoming calls we might have more than one call queued.
|
||||
// For now, we'll just assume the first call is the right information.
|
||||
// We'll probably really want to be getting this data from the
|
||||
// background worker on the desktop client.
|
||||
// Bug 990714 should fix this.
|
||||
if (!options.outgoing)
|
||||
sessionData = sessionData[0];
|
||||
|
||||
this.setReady(sessionData);
|
||||
}
|
||||
|
||||
if (options.outgoing) {
|
||||
client.requestCallInfo(this.get("loopToken"), handleResult.bind(this));
|
||||
}
|
||||
else {
|
||||
client.requestCallsInfo(this.get("loopVersion"),
|
||||
handleResult.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -54,6 +54,7 @@
|
||||
<script type="text/javascript" src="shared/libs/backbone-1.1.2.js"></script>
|
||||
|
||||
<!-- app scripts -->
|
||||
<script type="text/javascript" src="shared/js/client.js"></script>
|
||||
<script type="text/javascript" src="shared/js/models.js"></script>
|
||||
<script type="text/javascript" src="shared/js/views.js"></script>
|
||||
<script type="text/javascript" src="shared/js/router.js"></script>
|
||||
|
@ -61,7 +61,10 @@ loop.webapp = (function($, TB) {
|
||||
|
||||
initiate: function(event) {
|
||||
event.preventDefault();
|
||||
this.model.initiate(baseServerUrl);
|
||||
this.model.initiate({
|
||||
baseServerUrl: baseServerUrl,
|
||||
outgoing: true
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
192
browser/components/loop/test/shared/client_test.js
Normal file
192
browser/components/loop/test/shared/client_test.js
Normal file
@ -0,0 +1,192 @@
|
||||
/* 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 */
|
||||
|
||||
var expect = chai.expect;
|
||||
|
||||
describe("loop.shared.Client", function() {
|
||||
"use strict";
|
||||
|
||||
var sandbox, fakeXHR, requests = [], callback;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
fakeXHR = sandbox.useFakeXMLHttpRequest();
|
||||
requests = [];
|
||||
// https://github.com/cjohansen/Sinon.JS/issues/393
|
||||
fakeXHR.xhr.onCreate = function (xhr) {
|
||||
requests.push(xhr);
|
||||
};
|
||||
callback = sinon.spy();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("loop.shared.Client", function() {
|
||||
describe("#constructor", function() {
|
||||
it("should require a baseServerUrl setting", function() {
|
||||
expect(function() {
|
||||
new loop.shared.Client();
|
||||
}).to.Throw(Error, /required/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#requestCallUrl", function() {
|
||||
var client;
|
||||
|
||||
beforeEach(function() {
|
||||
client = new loop.shared.Client({baseServerUrl: "http://fake.api"});
|
||||
});
|
||||
|
||||
it("should post to /call-url/", function() {
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
expect(requests).to.have.length.of(1);
|
||||
expect(requests[0].method).to.be.equal("POST");
|
||||
expect(requests[0].url).to.be.equal("http://fake.api/call-url/");
|
||||
expect(requests[0].requestBody).to.be.equal('callerId=foo');
|
||||
|
||||
});
|
||||
|
||||
it("should request a call url", function() {
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
expect(requests).to.have.length.of(1);
|
||||
|
||||
requests[0].respond(200, {"Content-Type": "application/json"},
|
||||
'{"call_url": "fakeCallUrl"}');
|
||||
sinon.assert.calledWithExactly(callback, null, "fakeCallUrl");
|
||||
});
|
||||
|
||||
it("should send an error when the request fails", function() {
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
expect(requests).to.have.length.of(1);
|
||||
|
||||
requests[0].respond(400, {"Content-Type": "application/json"},
|
||||
'{"error": "my error"}');
|
||||
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
|
||||
return /400.*my error/.test(err.message);
|
||||
}));
|
||||
});
|
||||
|
||||
it("should send an error if the data is not valid", function() {
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
requests[0].respond(200, {"Content-Type": "application/json"},
|
||||
'{"bad": {}}');
|
||||
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
|
||||
return /Invalid data received/.test(err.message);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("#requestCallsInfo", function() {
|
||||
var client;
|
||||
|
||||
beforeEach(function() {
|
||||
client = new loop.shared.Client({baseServerUrl: "http://fake.api"});
|
||||
});
|
||||
|
||||
it("should prevent launching a conversation when version is missing",
|
||||
function() {
|
||||
expect(function() {
|
||||
client.requestCallsInfo();
|
||||
}).to.Throw(Error, /missing required parameter version/);
|
||||
});
|
||||
|
||||
it("should request data for all calls", function() {
|
||||
client.requestCallsInfo(42, callback);
|
||||
|
||||
expect(requests).to.have.length.of(1);
|
||||
expect(requests[0].url).to.be.equal("http://fake.api/calls?version=42");
|
||||
expect(requests[0].method).to.be.equal("GET");
|
||||
|
||||
requests[0].respond(200, {"Content-Type": "application/json"},
|
||||
'{"calls": [{"apiKey": "fake"}]}');
|
||||
sinon.assert.calledWithExactly(callback, null, [{apiKey: "fake"}]);
|
||||
});
|
||||
|
||||
it("should send an error when the request fails", function() {
|
||||
client.requestCallsInfo(42, callback);
|
||||
|
||||
requests[0].respond(400, {"Content-Type": "application/json"},
|
||||
'{"error": "my error"}');
|
||||
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
|
||||
return /400.*my error/.test(err.message);
|
||||
}));
|
||||
});
|
||||
|
||||
it("should send an error if the data is not valid", function() {
|
||||
client.requestCallsInfo(42, callback);
|
||||
|
||||
requests[0].respond(200, {"Content-Type": "application/json"},
|
||||
'{"bad": {}}');
|
||||
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
|
||||
return /Invalid data received/.test(err.message);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("requestCallInfo", function() {
|
||||
var client;
|
||||
|
||||
beforeEach(function() {
|
||||
client = new loop.shared.Client({baseServerUrl: "http://fake.api"});
|
||||
});
|
||||
|
||||
it("should prevent launching a conversation when token is missing",
|
||||
function() {
|
||||
expect(function() {
|
||||
client.requestCallInfo();
|
||||
}).to.Throw(Error, /missing.*[Tt]oken/);
|
||||
});
|
||||
|
||||
it("should post data for the given call", function() {
|
||||
client.requestCallInfo("fake", callback);
|
||||
|
||||
expect(requests).to.have.length.of(1);
|
||||
expect(requests[0].url).to.be.equal("http://fake.api/calls/fake");
|
||||
expect(requests[0].method).to.be.equal("POST");
|
||||
});
|
||||
|
||||
it("should receive call data for the given call", function() {
|
||||
client.requestCallInfo("fake", callback);
|
||||
|
||||
var sessionData = {
|
||||
sessionId: "one",
|
||||
sessionToken: "two",
|
||||
apiKey: "three"
|
||||
};
|
||||
|
||||
requests[0].respond(200, {"Content-Type": "application/json"},
|
||||
JSON.stringify(sessionData));
|
||||
sinon.assert.calledWithExactly(callback, null, sessionData);
|
||||
});
|
||||
|
||||
it("should send an error when the request fails", function() {
|
||||
client.requestCallInfo("fake", callback);
|
||||
|
||||
requests[0].respond(400, {"Content-Type": "application/json"},
|
||||
'{"error": "my error"}');
|
||||
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
|
||||
return /400.*my error/.test(err.message);
|
||||
}));
|
||||
});
|
||||
|
||||
it("should send an error if the data is not valid", function() {
|
||||
client.requestCallsInfo("fake", callback);
|
||||
|
||||
requests[0].respond(200, {"Content-Type": "application/json"},
|
||||
'{"bad": "one"}');
|
||||
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
|
||||
return /Invalid data received/.test(err.message);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -32,10 +32,13 @@
|
||||
</script>
|
||||
|
||||
<!-- App scripts -->
|
||||
<script src="../../content/shared/js/client.js"></script>
|
||||
<script src="../../content/shared/js/models.js"></script>
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/router.js"></script>
|
||||
|
||||
<!-- Test scripts -->
|
||||
<script src="client_test.js"></script>
|
||||
<script src="models_test.js"></script>
|
||||
<script src="views_test.js"></script>
|
||||
<script src="router_test.js"></script>
|
||||
|
@ -27,52 +27,75 @@ describe("loop.shared.models", function() {
|
||||
});
|
||||
|
||||
describe("ConversationModel", function() {
|
||||
var conversation, fakeSessionData, fakeBaseServerUrl;
|
||||
var conversation, reqCallInfoStub, reqCallsInfoStub,
|
||||
fakeSessionData, fakeBaseServerUrl;
|
||||
|
||||
beforeEach(function() {
|
||||
conversation = new sharedModels.ConversationModel();
|
||||
conversation.set("loopToken", "fakeToken");
|
||||
fakeSessionData = {
|
||||
sessionId: "sessionId",
|
||||
sessionToken: "sessionToken",
|
||||
apiKey: "apiKey"
|
||||
};
|
||||
fakeBaseServerUrl = "http://fakeBaseServerUrl";
|
||||
reqCallInfoStub = sandbox.stub(loop.shared.Client.prototype,
|
||||
"requestCallInfo");
|
||||
reqCallsInfoStub = sandbox.stub(loop.shared.Client.prototype,
|
||||
"requestCallsInfo");
|
||||
});
|
||||
|
||||
describe("#initiate", function() {
|
||||
it("should throw an Error if no baseServerUrl argument is passed",
|
||||
function () {
|
||||
expect(function() {
|
||||
conversation.initiate();
|
||||
}).to.Throw(Error, /baseServerUrl/);
|
||||
it("call requestCallInfo on the client for outgoing calls",
|
||||
function() {
|
||||
conversation.initiate({
|
||||
baseServerUrl: fakeBaseServerUrl,
|
||||
outgoing: true
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(reqCallInfoStub);
|
||||
sinon.assert.calledWith(reqCallInfoStub, "fakeToken");
|
||||
});
|
||||
|
||||
it("should prevent launching a conversation when token is missing",
|
||||
it("should not call requestCallsInfo on the client for outgoing calls",
|
||||
function() {
|
||||
expect(function() {
|
||||
conversation.initiate(fakeBaseServerUrl);
|
||||
}).to.Throw(Error, /missing required attribute loopToken/);
|
||||
conversation.initiate({
|
||||
baseServerUrl: fakeBaseServerUrl,
|
||||
outgoing: true
|
||||
});
|
||||
|
||||
sinon.assert.notCalled(reqCallsInfoStub);
|
||||
});
|
||||
|
||||
it("should make one ajax POST to a correctly constructed URL",
|
||||
it("call requestCallsInfo on the client for incoming calls",
|
||||
function() {
|
||||
conversation.set("loopToken", "fakeToken");
|
||||
conversation.initiate({
|
||||
baseServerUrl: fakeBaseServerUrl,
|
||||
outgoing: false
|
||||
});
|
||||
|
||||
conversation.initiate(fakeBaseServerUrl);
|
||||
sinon.assert.calledOnce(reqCallsInfoStub);
|
||||
sinon.assert.calledWith(reqCallsInfoStub);
|
||||
});
|
||||
|
||||
expect(requests).to.have.length.of(1);
|
||||
expect(requests[0].method.toLowerCase()).to.equal("post");
|
||||
expect(requests[0].url).to.match(
|
||||
new RegExp("^" + fakeBaseServerUrl + "/calls/fakeToken"));
|
||||
it("should not call requestCallInfo on the client for incoming calls",
|
||||
function() {
|
||||
conversation.initiate({
|
||||
baseServerUrl: fakeBaseServerUrl,
|
||||
outgoing: false
|
||||
});
|
||||
|
||||
sinon.assert.notCalled(reqCallInfoStub);
|
||||
});
|
||||
|
||||
it("should update conversation session information from server data",
|
||||
function() {
|
||||
conversation.set("loopToken", "fakeToken");
|
||||
conversation.initiate(fakeBaseServerUrl);
|
||||
reqCallInfoStub.callsArgWith(1, null, fakeSessionData);
|
||||
|
||||
requests[0].respond(200, {"Content-Type": "application/json"},
|
||||
JSON.stringify(fakeSessionData));
|
||||
conversation.initiate({
|
||||
baseServerUrl: fakeBaseServerUrl,
|
||||
outgoing: true
|
||||
});
|
||||
|
||||
expect(conversation.get("sessionId")).eql("sessionId");
|
||||
expect(conversation.get("sessionToken")).eql("sessionToken");
|
||||
@ -81,27 +104,29 @@ describe("loop.shared.models", function() {
|
||||
|
||||
it("should trigger session:ready without fetching session data over "+
|
||||
"HTTP when already set", function(done) {
|
||||
sandbox.stub(jQuery, "ajax");
|
||||
conversation.set("loopToken", "fakeToken");
|
||||
conversation.set(fakeSessionData);
|
||||
|
||||
conversation.on("session:ready", function() {
|
||||
sinon.assert.notCalled($.ajax);
|
||||
sinon.assert.notCalled(reqCallInfoStub);
|
||||
done();
|
||||
}).initiate(fakeBaseServerUrl);
|
||||
}).initiate({
|
||||
baseServerUrl: fakeBaseServerUrl,
|
||||
outgoing: true
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it("should trigger a `session:error` on failure", function(done) {
|
||||
conversation.set("loopToken", "fakeToken");
|
||||
conversation.initiate(fakeBaseServerUrl);
|
||||
reqCallInfoStub.callsArgWith(1,
|
||||
new Error("failed: HTTP 400 Bad Request; fake"));
|
||||
|
||||
conversation.on("session:error", function(err) {
|
||||
expect(err.message).to.match(/failed: HTTP 400 Bad Request; fake/);
|
||||
done();
|
||||
}).initiate({
|
||||
baseServerUrl: fakeBaseServerUrl,
|
||||
outgoing: true
|
||||
});
|
||||
|
||||
requests[0].respond(400, {"Content-Type": "application/json"},
|
||||
JSON.stringify({error: "fake"}));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -38,16 +38,6 @@ describe("loop.webapp", function() {
|
||||
new loop.webapp.WebappRouter();
|
||||
}).to.Throw(Error, /missing required conversation/);
|
||||
});
|
||||
|
||||
it("should load the HomeView", function() {
|
||||
sandbox.stub(loop.webapp.WebappRouter.prototype, "loadView");
|
||||
|
||||
var router = new loop.webapp.WebappRouter({conversation: conversation});
|
||||
|
||||
sinon.assert.calledOnce(router.loadView);
|
||||
sinon.assert.calledWithMatch(router.loadView,
|
||||
{$el: {selector: "#home"}});
|
||||
});
|
||||
});
|
||||
|
||||
describe("constructed", function() {
|
||||
@ -164,6 +154,12 @@ describe("loop.webapp", function() {
|
||||
|
||||
sinon.assert.calledOnce(fakeSubmitEvent.preventDefault);
|
||||
sinon.assert.calledOnce(initiate);
|
||||
// XXX host should be configurable
|
||||
// see https://bugzilla.mozilla.org/show_bug.cgi?id=987086
|
||||
sinon.assert.calledWith(initiate, {
|
||||
baseServerUrl: "http://localhost:5000",
|
||||
outgoing: true
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user