gecko-dev/browser/components/loop/test/shared/client_test.js

291 lines
9.5 KiB
JavaScript

/* 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, hawk */
var expect = chai.expect;
describe("loop.shared.Client", function() {
"use strict";
var sandbox,
fakeXHR,
requests = [],
callback,
mozLoop,
fakeToken;
var fakeErrorRes = JSON.stringify({
status: "errors",
errors: [{
location: "url",
name: "token",
description: "invalid token"
}]
});
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();
fakeToken = "fakeTokenText";
});
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() {
window.navigator.mozLoop = {
ensureRegistered: sinon.stub().callsArgWith(0, null),
noteCallUrlExpiry: sinon.spy(),
getLoopCharPref: sandbox.stub()
.returns(null)
.withArgs("hawk-session-token")
.returns(fakeToken)
};
client = new loop.shared.Client(
{baseServerUrl: "http://fake.api", mozLoop: window.navigator.mozLoop}
);
});
it("should ensure loop is registered", function() {
client.requestCallUrl("foo", callback);
sinon.assert.calledOnce(navigator.mozLoop.ensureRegistered);
});
it("should send an error when registration fails", function() {
navigator.mozLoop.ensureRegistered.callsArgWith(0, "offline");
client.requestCallUrl("foo", callback);
sinon.assert.calledOnce(callback);
sinon.assert.calledWithExactly(callback, "offline");
});
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 set the XHR Authorization header", function() {
sandbox.stub(hawk.client, "header").returns( {field: fakeToken} );
client._credentials = {
// XXX we probably really want to stub out external module calls
// eg deriveHawkCredentials, rather supplying them with valid arguments
// like we're doing here:
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
user: 'Steve'
};
client.requestCallUrl("foo", callback);
expect(requests[0].requestHeaders.Authorization).to.equal(fakeToken);
});
it("should request a call url", function() {
var callUrlData = {
"call_url": "fakeCallUrl",
"expiresAt": 60
};
client.requestCallUrl("foo", callback);
requests[0].respond(200, {"Content-Type": "application/json"},
JSON.stringify(callUrlData));
sinon.assert.calledWithExactly(callback, null, callUrlData);
});
it("should note the call url expiry", function() {
var callUrlData = {
"call_url": "fakeCallUrl",
"expiresAt": 60
};
client.requestCallUrl("foo", callback);
requests[0].respond(200, {"Content-Type": "application/json"},
JSON.stringify(callUrlData));
// expiresAt is in hours, and noteCallUrlExpiry wants seconds.
sinon.assert.calledWithExactly(navigator.mozLoop.noteCallUrlExpiry,
60 * 60 * 60);
});
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"},
fakeErrorRes);
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
return /400.*invalid token/.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() {
mozLoop = {
getLoopCharPref: sandbox.stub()
.returns(null)
.withArgs("hawk-session-token")
.returns(fakeToken)
};
client = new loop.shared.Client(
{baseServerUrl: "http://fake.api", mozLoop: mozLoop}
);
});
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 set the XHR Authorization header", function() {
sandbox.stub(hawk.client, "header").returns( {field: fakeToken} );
// XXX we probably really want to stub out external module calls
// eg deriveHawkCredentials, rather supplying them with valid arguments
// like we're doing here:
client._credentials = {
key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn',
algorithm: 'sha256',
user: 'Steve'
};
client.requestCallsInfo("foo", callback);
expect(requests[0].requestHeaders.Authorization).to.equal(fakeToken);
});
it("should send an error when the request fails", function() {
client.requestCallsInfo(42, callback);
requests[0].respond(400, {"Content-Type": "application/json"},
fakeErrorRes);
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
return /400.*invalid token/.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", mozLoop: undefined}
);
});
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"},
fakeErrorRes);
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
return /400.*invalid token/.test(err.message);
}));
});
it("should send an error if the data is not valid", function() {
client.requestCallInfo("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);
}));
});
});
});
});