blob: 1fb7856f1193b34ed5e7b5f7757ad857d75e968d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview
*/
(function() {
'use strict';
/** @type {sinon.FakeXhrCtrl} */
var fakeXhrCtrl;
/** @type {sinon.FakeXhr} */
var fakeXhr;
QUnit.module('xhr', {
beforeEach: function() {
fakeXhr = null;
fakeXhrCtrl = sinon.useFakeXMLHttpRequest();
fakeXhrCtrl.onCreate =
function(/** sinon.FakeXhr */ xhr) {
fakeXhr = xhr;
};
remoting.identity = new remoting.Identity();
chromeMocks.identity.mock$setToken('my_token');
},
afterEach: function() {
remoting.identity = null;
fakeXhrCtrl.restore();
}
});
QUnit.test('urlencodeParamHash', function(assert) {
assert.equal(
remoting.Xhr.urlencodeParamHash({}),
'');
assert.equal(
remoting.Xhr.urlencodeParamHash({'key': 'value'}),
'key=value');
assert.equal(
remoting.Xhr.urlencodeParamHash({'key /?=&': 'value /?=&'}),
'key%20%2F%3F%3D%26=value%20%2F%3F%3D%26');
assert.equal(
remoting.Xhr.urlencodeParamHash({'k1': 'v1', 'k2': 'v2'}),
'k1=v1&k2=v2');
});
//
// Test for what happens when the parameters are specified
// incorrectly.
//
QUnit.test('invalid *content parameters', function(assert) {
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
jsonContent: {},
formContent: {}
});
});
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
textContent: '',
formContent: {}
});
});
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
textContent: '',
jsonContent: {}
});
});
});
QUnit.test('invalid URL parameters', function(assert) {
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com?',
urlParams: {}
});
});
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com#',
urlParams: {}
});
});
});
QUnit.test('invalid auth parameters', function(assert) {
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
useIdentity: false,
oauthToken: '',
headers: {
'Authorization': ''
}
});
});
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
useIdentity: true,
headers: {
'Authorization': ''
}
});
});
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
useIdentity: true,
oauthToken: '',
headers: {}
});
});
});
QUnit.test('invalid auth parameters', function(assert) {
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
useIdentity: false,
oauthToken: '',
headers: {
'Authorization': ''
}
});
});
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
useIdentity: true,
headers: {
'Authorization': ''
}
});
});
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
useIdentity: true,
oauthToken: '',
headers: {}
});
});
});
QUnit.test('unexpected parameters', function(assert) {
assert.throws(function() {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
xyzzy: 'not a real parameter'
});
});
});
//
// The typical case.
//
QUnit.test('successful GET', function(assert) {
var promise = new remoting.Xhr({
method: 'GET',
url: 'http://foo.com'
}).start().then(function(response) {
assert.ok(!response.isError());
assert.equal(response.status, 200);
assert.equal(response.getText(), 'body');
});
assert.equal(fakeXhr.method, 'GET');
assert.equal(fakeXhr.url, 'http://foo.com');
assert.equal(fakeXhr.withCredentials, false);
assert.equal(fakeXhr.requestBody, null);
assert.ok(!('Content-type' in fakeXhr.requestHeaders));
fakeXhr.respond(200, {}, 'body');
return promise;
});
//
// Tests for the effect of acceptJson.
//
QUnit.test('acceptJson required', function(assert) {
var promise = new remoting.Xhr({
method: 'GET',
url: 'http://foo.com'
}).start().then(function(response) {
assert.throws(response.getJson);
});
fakeXhr.respond(200, {}, '{}');
return promise;
});
QUnit.test('JSON response', function(assert) {
var responseJson = {
'myJsonData': [true]
};
var responseText = JSON.stringify(responseJson);
var promise = new remoting.Xhr({
method: 'GET',
url: 'http://foo.com',
acceptJson: true
}).start().then(function(response) {
// Calling getText is still OK even when a JSON response is
// requested.
assert.equal(
response.getText(),
responseText);
// Check that getJson works as advertised.
assert.deepEqual(
response.getJson(),
responseJson);
// Calling getJson multiple times doesn't re-parse the response.
assert.strictEqual(
response.getJson(),
response.getJson());
});
fakeXhr.respond(200, {}, responseText);
return promise;
});
//
// Tests for various parameters that modify the HTTP request.
//
QUnit.test('GET with param string', function(assert) {
new remoting.Xhr({
method: 'GET',
url: 'http://foo.com',
urlParams: 'the_param_string'
}).start();
assert.equal(fakeXhr.url, 'http://foo.com?the_param_string');
});
QUnit.test('GET with param object', function(assert) {
new remoting.Xhr({
method: 'GET',
url: 'http://foo.com',
urlParams: {'a': 'b', 'c': 'd'}
}).start();
assert.equal(fakeXhr.url, 'http://foo.com?a=b&c=d');
});
QUnit.test('GET with headers', function(assert) {
new remoting.Xhr({
method: 'GET',
url: 'http://foo.com',
headers: {'Header1': 'headerValue1', 'Header2': 'headerValue2'}
}).start();
assert.equal(
fakeXhr.requestHeaders['Header1'],
'headerValue1');
assert.equal(
fakeXhr.requestHeaders['Header2'],
'headerValue2');
});
QUnit.test('GET with credentials', function(assert) {
new remoting.Xhr({
method: 'GET',
url: 'http://foo.com',
withCredentials: true
}).start();
assert.equal(fakeXhr.withCredentials, true);
});
//
// Checking that typical POST requests work.
//
QUnit.test('POST with text content', function(assert) {
var done = assert.async();
var promise = new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
textContent: 'the_content_string'
}).start().then(function(response) {
assert.equal(response.status, 200);
assert.equal(response.getText(), 'body');
done();
});
assert.equal(fakeXhr.method, 'POST');
assert.equal(fakeXhr.url, 'http://foo.com');
assert.equal(fakeXhr.withCredentials, false);
assert.equal(fakeXhr.requestBody, 'the_content_string');
assert.equal(fakeXhr.requestHeaders['Content-type'],
'text/plain; charset=UTF-8');
fakeXhr.respond(200, {}, 'body');
return promise;
});
QUnit.test('POST with form content', function(assert) {
new remoting.Xhr({
method: 'POST',
url: 'http://foo.com',
formContent: {'a': 'b', 'c': 'd'}
}).start();
assert.equal(fakeXhr.requestBody, 'a=b&c=d');
assert.equal(
fakeXhr.requestHeaders['Content-type'],
'application/x-www-form-urlencoded; charset=UTF-8');
});
//
// Tests for authentication-related options.
//
QUnit.test('GET with auth token', function(assert) {
new remoting.Xhr({
method: 'GET',
url: 'http://foo.com',
oauthToken: 'my_token'
}).start();
assert.equal(fakeXhr.requestHeaders['Authorization'],
'Bearer my_token');
});
QUnit.test('GET with useIdentity', function(assert) {
var xhr = new remoting.Xhr({
method: 'GET',
url: 'http://foo.com',
useIdentity: true
});
xhr.start();
var done = assert.async();
fakeXhr.addEventListener('loadstart', function() {
assert.equal(fakeXhr.requestHeaders['Authorization'],
'Bearer my_token');
done();
});
});
//
// Error responses.
//
QUnit.test('GET with error response', function(assert) {
var promise = new remoting.Xhr({
method: 'GET',
url: 'http://foo.com'
}).start().then(function(response) {
assert.ok(response.isError());
assert.equal(response.status, 500);
assert.equal(response.getText(), 'body');
});
fakeXhr.respond(500, {}, 'body');
return promise;
});
QUnit.test('204 is not an error', function(assert) {
var promise = new remoting.Xhr({
method: 'GET',
url: 'http://foo.com'
}).start().then(function(response) {
assert.ok(!response.isError());
assert.equal(response.status, 204);
assert.equal(response.getText(), '');
});
fakeXhr.respond(204, {}, null);
return promise;
});
QUnit.test('GET with non-HTTP response', function(assert) {
var promise = new remoting.Xhr({
method: 'GET',
url: 'http://foo.com'
}).start().then(function(response) {
assert.ok(response.isError());
});
fakeXhr.respond(0, {}, null);
return promise;
});
QUnit.module('AutoRetryXhr', {
beforeEach: function() {
fakeXhr = null;
fakeXhrCtrl = sinon.useFakeXMLHttpRequest();
fakeXhrCtrl.onCreate = function(/** sinon.FakeXhr */ xhr) {
fakeXhr = xhr;
};
},
afterEach: function() {
fakeXhrCtrl.restore();
}
});
/**
* A class allows you to specify a sequence of canned responses, to be returned
* in order in response to XHRs.
*
* @param {Array<Array>} cannedResponses
* @constructor
*/
var CannedXhrResponder = function(cannedResponses) {
/** @private */
this.mockXhr_ = sinon.useFakeXMLHttpRequest();
this.cannedResponses_ = cannedResponses;
this.mockXhr_.onCreate = this.onCreate_.bind(this);
};
/** @private */
CannedXhrResponder.prototype.onCreate_ = function(/** sinon.FakeXhr */ xhr) {
var that = this;
var response = that.cannedResponses_.shift();
Promise.resolve().then(function() {
xhr.respond.apply(xhr, response);
});
};
CannedXhrResponder.prototype.dispose = function() {
this.mockXhr_.restore();
};
QUnit.test('retries on status code equals 0',
/**
* 'this' is not defined for jscompile, so it can't figure out the type of
* this.clock.
* @suppress {reportUnknownTypes|checkVars|checkTypes}
*/
function(assert) {
this.clock.restore();
fakeXhrCtrl.restore();
var responder = new CannedXhrResponder([
[0, {}, null],
[0, {}, null],
[200, {}, 'body']
]);
var promise = new remoting.AutoRetryXhr({
method: 'GET',
url: 'http://foo.com'
}).start().then(function(response) {
assert.ok(!response.isError());
}).catch(function(){
throw new Error('Expect retry to succeed.');
});
return promise;
});
QUnit.test('respects opt_maxRetryAttempts',
/**
* 'this' is not defined for jscompile, so it can't figure out the type of
* this.clock.
* @suppress {reportUnknownTypes|checkVars|checkTypes}
*/
function(assert) {
this.clock.restore();
fakeXhrCtrl.restore();
var responder = new CannedXhrResponder([
[0, {}, null],
[0, {}, null],
[0, {}, null],
[200, {}, 'body']
]);
var promise = new remoting.AutoRetryXhr({
method: 'GET',
url: 'http://foo.com'
}, 2).start().then(function(response) {
throw new Error('Expect retry to fail.');
}).catch(function(){
assert.ok(true);
});
return promise;
});
QUnit.test('does not retry when offline', function(assert) {
var isOnlineStub = sinon.stub(base, 'isOnline');
isOnlineStub.returns(false);
var promise = new remoting.AutoRetryXhr({
method: 'GET',
url: 'http://foo.com'
}).start().then(function(response) {
assert.ok(false, 'Expect failure');
}).catch(function(/** remoting.Error */ error) {
assert.equal(error.getTag(), remoting.Error.Tag.NETWORK_FAILURE);
});
isOnlineStub.restore();
return promise;
});
QUnit.test('resolves with successful responses', function(assert) {
var promise = new remoting.AutoRetryXhr({
method: 'GET',
url: 'http://foo.com'
}).start().then(function(response) {
assert.ok(!response.isError());
assert.equal(response.getText(), 'body');
});
fakeXhr.respond(200, {}, 'body');
return promise;
});
QUnit.test('rejects with failed responses', function(assert) {
var promise = new remoting.AutoRetryXhr({
method: 'GET',
url: 'http://foo.com'
}).start().then(function(response) {
assert.ok(response.isError());
assert.equal(response.getText(), 'failure');
});
fakeXhr.respond(500, {}, 'failure');
return promise;
});
})();