4df24d7e81
This adds the ability to mock responseURL, which is used to detect the ultimate request URL when following 302 redirects. For more information, see: https://xhr.spec.whatwg.org/#the-responseurl-attribute Closes jasmine/jasmine-ajax#145
550 lines
20 KiB
JavaScript
550 lines
20 KiB
JavaScript
/*global sharedAjaxResponseBehaviorForZepto_Failure: true, sharedAjaxResponseBehaviorForZepto_Success: true */
|
|
|
|
describe("Jasmine Mock Ajax (for toplevel)", function() {
|
|
var request, anotherRequest, response;
|
|
var success, error, complete;
|
|
var client, onreadystatechange;
|
|
var sharedContext = {};
|
|
var fakeGlobal, mockAjax;
|
|
|
|
beforeEach(function() {
|
|
var fakeXMLHttpRequest = jasmine.createSpy('realFakeXMLHttpRequest');
|
|
fakeGlobal = {
|
|
XMLHttpRequest: fakeXMLHttpRequest,
|
|
DOMParser: window.DOMParser,
|
|
ActiveXObject: window.ActiveXObject
|
|
};
|
|
mockAjax = new window.MockAjax(fakeGlobal);
|
|
mockAjax.install();
|
|
|
|
success = jasmine.createSpy("onSuccess");
|
|
error = jasmine.createSpy("onFailure");
|
|
complete = jasmine.createSpy("onComplete");
|
|
|
|
onreadystatechange = function() {
|
|
if (this.readyState === (this.DONE || 4)) { // IE 8 doesn't support DONE
|
|
if (this.status === 200) {
|
|
success(this.responseText, this.textStatus, this);
|
|
} else {
|
|
error(this, this.textStatus, '');
|
|
}
|
|
|
|
complete(this, this.textStatus);
|
|
}
|
|
};
|
|
});
|
|
|
|
describe("when making a request", function () {
|
|
beforeEach(function() {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.send();
|
|
request = mockAjax.requests.mostRecent();
|
|
});
|
|
|
|
it("should store URL and transport", function() {
|
|
expect(request.url).toEqual("example.com/someApi");
|
|
});
|
|
|
|
it("should queue the request", function() {
|
|
expect(mockAjax.requests.count()).toEqual(1);
|
|
});
|
|
|
|
it("should allow access to the queued request", function() {
|
|
expect(mockAjax.requests.first()).toEqual(request);
|
|
});
|
|
|
|
it("should allow access to the queued request via index", function() {
|
|
expect(mockAjax.requests.at(0)).toEqual(request);
|
|
});
|
|
|
|
describe("and then another request", function () {
|
|
beforeEach(function() {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.send();
|
|
|
|
anotherRequest = mockAjax.requests.mostRecent();
|
|
});
|
|
|
|
it("should queue the next request", function() {
|
|
expect(mockAjax.requests.count()).toEqual(2);
|
|
});
|
|
|
|
it("should allow access to the other queued request", function() {
|
|
expect(mockAjax.requests.first()).toEqual(request);
|
|
expect(mockAjax.requests.mostRecent()).toEqual(anotherRequest);
|
|
});
|
|
});
|
|
|
|
describe("mockAjax.requests.mostRecent()", function () {
|
|
|
|
describe("when there is one request queued", function () {
|
|
it("should return the request", function() {
|
|
expect(mockAjax.requests.mostRecent()).toEqual(request);
|
|
});
|
|
});
|
|
|
|
describe("when there is more than one request", function () {
|
|
beforeEach(function() {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.send();
|
|
anotherRequest = mockAjax.requests.mostRecent();
|
|
});
|
|
|
|
it("should return the most recent request", function() {
|
|
expect(mockAjax.requests.mostRecent()).toEqual(anotherRequest);
|
|
});
|
|
});
|
|
|
|
describe("when there are no requests", function () {
|
|
beforeEach(function() {
|
|
mockAjax.requests.reset();
|
|
});
|
|
|
|
it("should return null", function() {
|
|
expect(mockAjax.requests.mostRecent()).toBeUndefined();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("clearAjaxRequests()", function () {
|
|
beforeEach(function() {
|
|
mockAjax.requests.reset();
|
|
});
|
|
|
|
it("should remove all requests", function() {
|
|
expect(mockAjax.requests.count()).toEqual(0);
|
|
expect(mockAjax.requests.mostRecent()).toBeUndefined();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when simulating a response with request.response", function () {
|
|
describe("and the response is Success", function () {
|
|
beforeEach(function() {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.setRequestHeader("Content-Type", "text/plain");
|
|
client.send();
|
|
|
|
request = mockAjax.requests.mostRecent();
|
|
response = {status: 200, statusText: "OK", contentType: "text/html", responseText: "OK!"};
|
|
request.respondWith(response);
|
|
|
|
sharedContext.responseCallback = success;
|
|
sharedContext.status = response.status;
|
|
sharedContext.statusText = response.statusText;
|
|
sharedContext.contentType = response.contentType;
|
|
sharedContext.responseText = response.responseText;
|
|
sharedContext.responseType = response.responseType;
|
|
sharedContext.responseURL = response.responseURL;
|
|
});
|
|
|
|
it("should call the success handler", function() {
|
|
expect(success).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should not call the failure handler", function() {
|
|
expect(error).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call the complete handler", function() {
|
|
expect(complete).toHaveBeenCalled();
|
|
});
|
|
|
|
sharedAjaxResponseBehaviorForZepto_Success(sharedContext);
|
|
});
|
|
|
|
describe("and the response is Success, but with JSON", function () {
|
|
beforeEach(function() {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.setRequestHeader("Content-Type", "application/json");
|
|
client.send();
|
|
|
|
request = mockAjax.requests.mostRecent();
|
|
var responseObject = {status: 200, statusText: "OK", contentType: "application/json", responseText: '{"foo":"bar"}', responseType: "json"};
|
|
|
|
request.respondWith(responseObject);
|
|
|
|
sharedContext.responseCallback = success;
|
|
sharedContext.status = responseObject.status;
|
|
sharedContext.statusText = responseObject.statusText;
|
|
sharedContext.contentType = responseObject.contentType;
|
|
sharedContext.responseText = responseObject.responseText;
|
|
sharedContext.responseType = responseObject.responseType;
|
|
sharedContext.responseURL = responseObject.responseURL;
|
|
|
|
response = success.calls.mostRecent().args[2];
|
|
});
|
|
|
|
it("should call the success handler", function() {
|
|
expect(success).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should not call the failure handler", function() {
|
|
expect(error).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call the complete handler", function() {
|
|
expect(complete).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should return a JavaScript object for XHR2 response", function() {
|
|
var responseText = sharedContext.responseText;
|
|
expect(success.calls.mostRecent().args[0]).toEqual(responseText);
|
|
|
|
expect(response.responseText).toEqual(responseText);
|
|
expect(response.response).toEqual({foo: "bar"});
|
|
});
|
|
|
|
sharedAjaxResponseBehaviorForZepto_Success(sharedContext);
|
|
});
|
|
|
|
describe("and the response is Success, and response is overriden", function () {
|
|
beforeEach(function() {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.setRequestHeader("Content-Type", "application/json");
|
|
client.send();
|
|
|
|
request = mockAjax.requests.mostRecent();
|
|
var responseObject = {status: 200, statusText: "OK", contentType: "application/json", responseText: '{"foo":"bar"}', responseType: 'json'};
|
|
|
|
request.respondWith(responseObject);
|
|
|
|
sharedContext.responseCallback = success;
|
|
sharedContext.status = responseObject.status;
|
|
sharedContext.statusText = responseObject.statusText;
|
|
sharedContext.contentType = responseObject.contentType;
|
|
sharedContext.responseText = responseObject.responseText;
|
|
sharedContext.responseType = responseObject.responseType;
|
|
sharedContext.responseURL = responseObject.responseURL;
|
|
|
|
response = success.calls.mostRecent().args[2];
|
|
});
|
|
|
|
it("should return the provided override for the XHR2 response", function() {
|
|
var responseText = sharedContext.responseText;
|
|
|
|
expect(response.responseText).toEqual(responseText);
|
|
expect(response.response).toEqual({foo: "bar"});
|
|
});
|
|
|
|
sharedAjaxResponseBehaviorForZepto_Success(sharedContext);
|
|
});
|
|
|
|
describe("and the response is Success, and responseURL is set", function () {
|
|
beforeEach(function() {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.setRequestHeader("Content-Type", "application/json");
|
|
client.send();
|
|
|
|
request = mockAjax.requests.mostRecent();
|
|
var responseObject = {status: 200, statusText: "OK", contentType: "application/json", responseText: '{"foo":"bar"}', responseType: 'json', responseURL: 'example.com/redirected'};
|
|
|
|
request.respondWith(responseObject);
|
|
|
|
sharedContext.responseCallback = success;
|
|
sharedContext.status = responseObject.status;
|
|
sharedContext.statusText = responseObject.statusText;
|
|
sharedContext.contentType = responseObject.contentType;
|
|
sharedContext.responseText = responseObject.responseText;
|
|
sharedContext.responseType = responseObject.responseType;
|
|
sharedContext.responseURL = responseObject.responseURL;
|
|
|
|
response = success.calls.mostRecent().args[2];
|
|
});
|
|
|
|
sharedAjaxResponseBehaviorForZepto_Success(sharedContext);
|
|
});
|
|
|
|
describe("response with unique header names using an object", function () {
|
|
beforeEach(function () {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com");
|
|
client.send();
|
|
|
|
request = mockAjax.requests.mostRecent();
|
|
var responseObject = {status: 200, statusText: "OK", responseText: '["foo"]', responseHeaders: {
|
|
'X-Header1': 'header 1 value',
|
|
'X-Header2': 'header 2 value',
|
|
'X-Header3': 'header 3 value'
|
|
}};
|
|
request.respondWith(responseObject);
|
|
response = success.calls.mostRecent().args[2];
|
|
});
|
|
|
|
it("getResponseHeader should return the each value", function () {
|
|
expect(response.getResponseHeader('X-Header1')).toBe('header 1 value');
|
|
expect(response.getResponseHeader('X-Header2')).toBe('header 2 value');
|
|
expect(response.getResponseHeader('X-Header3')).toBe('header 3 value');
|
|
});
|
|
|
|
it("getAllResponseHeaders should return all values", function () {
|
|
expect(response.getAllResponseHeaders()).toBe([
|
|
"X-Header1: header 1 value",
|
|
"X-Header2: header 2 value",
|
|
"X-Header3: header 3 value"
|
|
].join("\r\n") + "\r\n");
|
|
});
|
|
});
|
|
|
|
describe("response with multiple headers of the same name using an array of objects", function () {
|
|
beforeEach(function () {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com");
|
|
client.send();
|
|
|
|
request = mockAjax.requests.mostRecent();
|
|
var responseObject = {status: 200, statusText: "OK", responseText: '["foo"]', responseHeaders: [
|
|
{ name: 'X-Header', value: 'header value 1' },
|
|
{ name: 'X-Header', value: 'header value 2' }
|
|
]};
|
|
request.respondWith(responseObject);
|
|
response = success.calls.mostRecent().args[2];
|
|
});
|
|
|
|
it("getResponseHeader should return all values comma separated", function () {
|
|
expect(response.getResponseHeader('X-Header')).toBe('header value 1, header value 2');
|
|
});
|
|
|
|
it("getAllResponseHeaders should return all values", function () {
|
|
expect(response.getAllResponseHeaders()).toBe([
|
|
"X-Header: header value 1",
|
|
"X-Header: header value 2"
|
|
].join("\r\n") + "\r\n");
|
|
});
|
|
});
|
|
|
|
describe("the content type defaults to application/json", function () {
|
|
beforeEach(function() {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.setRequestHeader("Content-Type", "application/json");
|
|
client.send();
|
|
|
|
request = mockAjax.requests.mostRecent();
|
|
response = {status: 200, statusText: "OK", responseText: '{"foo": "valid JSON, dammit."}', responseType: 'json'};
|
|
request.respondWith(response);
|
|
|
|
sharedContext.responseCallback = success;
|
|
sharedContext.status = response.status;
|
|
sharedContext.statusText = response.statusText;
|
|
sharedContext.contentType = "application/json";
|
|
sharedContext.responseType = response.responseType;
|
|
sharedContext.responseText = response.responseText;
|
|
sharedContext.responseURL = response.responseURL;
|
|
});
|
|
|
|
it("should call the success handler", function() {
|
|
expect(success).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should not call the failure handler", function() {
|
|
expect(error).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call the complete handler", function() {
|
|
expect(complete).toHaveBeenCalled();
|
|
});
|
|
|
|
sharedAjaxResponseBehaviorForZepto_Success(sharedContext);
|
|
});
|
|
|
|
describe("and the status/response code is 0", function () {
|
|
beforeEach(function() {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.setRequestHeader("Content-Type", "text/plain");
|
|
client.send();
|
|
|
|
request = mockAjax.requests.mostRecent();
|
|
response = {status: 0, statusText: "ABORT", responseText: '{"foo": "whoops!"}'};
|
|
request.respondWith(response);
|
|
|
|
sharedContext.responseCallback = error;
|
|
sharedContext.status = 0;
|
|
sharedContext.statusText = response.statusText;
|
|
sharedContext.contentType = 'application/json';
|
|
sharedContext.responseText = response.responseText;
|
|
sharedContext.responseType = response.responseType;
|
|
sharedContext.responseURL = response.responseURL;
|
|
});
|
|
|
|
it("should call the success handler", function() {
|
|
expect(success).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should not call the failure handler", function() {
|
|
expect(error).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call the complete handler", function() {
|
|
expect(complete).toHaveBeenCalled();
|
|
});
|
|
|
|
sharedAjaxResponseBehaviorForZepto_Failure(sharedContext);
|
|
});
|
|
});
|
|
|
|
describe("and the response is error", function () {
|
|
beforeEach(function() {
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.setRequestHeader("Content-Type", "text/plain");
|
|
client.send();
|
|
|
|
request = mockAjax.requests.mostRecent();
|
|
response = {status: 500, statusText: "SERVER ERROR", contentType: "text/html", responseText: "(._){"};
|
|
request.respondWith(response);
|
|
|
|
sharedContext.responseCallback = error;
|
|
sharedContext.status = response.status;
|
|
sharedContext.statusText = response.statusText;
|
|
sharedContext.contentType = response.contentType;
|
|
sharedContext.responseText = response.responseText;
|
|
sharedContext.responseType = response.responseType;
|
|
sharedContext.responseURL = response.responseURL;
|
|
});
|
|
|
|
it("should not call the success handler", function() {
|
|
expect(success).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call the failure handler", function() {
|
|
expect(error).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call the complete handler", function() {
|
|
expect(complete).toHaveBeenCalled();
|
|
});
|
|
|
|
sharedAjaxResponseBehaviorForZepto_Failure(sharedContext);
|
|
});
|
|
|
|
describe('when simulating a response with request.responseTimeout', function() {
|
|
beforeEach(function() {
|
|
jasmine.clock().install();
|
|
|
|
client = new fakeGlobal.XMLHttpRequest();
|
|
client.onreadystatechange = onreadystatechange;
|
|
client.open("GET", "example.com/someApi");
|
|
client.setRequestHeader("Content-Type", "text/plain");
|
|
client.send();
|
|
|
|
request = mockAjax.requests.mostRecent();
|
|
response = {contentType: "text/html", response: "(._){response", responseText: "(._){", responseType: "text"};
|
|
request.responseTimeout(response);
|
|
|
|
sharedContext.responseCallback = error;
|
|
sharedContext.status = response.status;
|
|
sharedContext.statusText = response.statusText;
|
|
sharedContext.contentType = response.contentType;
|
|
sharedContext.responseText = response.responseText;
|
|
sharedContext.responseType = response.responseType;
|
|
sharedContext.responseURL = response.responseURL;
|
|
});
|
|
|
|
afterEach(function() {
|
|
jasmine.clock().uninstall();
|
|
});
|
|
|
|
it("should not call the success handler", function() {
|
|
expect(success).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call the failure handler", function() {
|
|
expect(error).toHaveBeenCalled();
|
|
});
|
|
|
|
it("should call the complete handler", function() {
|
|
expect(complete).toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
function sharedAjaxResponseBehaviorForZepto_Success(context) {
|
|
describe("the success response", function () {
|
|
var xhr;
|
|
beforeEach(function() {
|
|
xhr = context.responseCallback.calls.mostRecent().args[2];
|
|
});
|
|
|
|
it("should have the expected status code", function() {
|
|
expect(xhr.status).toEqual(context.status);
|
|
});
|
|
|
|
it("should have the expected content type", function() {
|
|
expect(xhr.getResponseHeader('Content-Type')).toEqual(context.contentType);
|
|
});
|
|
|
|
it("should have the expected xhr2 response", function() {
|
|
var expected = context.response || context.responseType === 'json' ? JSON.parse(context.responseText) : context.responseText;
|
|
expect(xhr.response).toEqual(expected);
|
|
});
|
|
|
|
it("should have the expected response text", function() {
|
|
expect(xhr.responseText).toEqual(context.responseText);
|
|
});
|
|
|
|
it("should have the expected status text", function() {
|
|
expect(xhr.statusText).toEqual(context.statusText);
|
|
});
|
|
|
|
it("should have the expected response URL", function() {
|
|
expect(xhr.responseURL).toEqual(context.responseURL || null);
|
|
});
|
|
});
|
|
}
|
|
|
|
function sharedAjaxResponseBehaviorForZepto_Failure(context) {
|
|
describe("the failure response", function () {
|
|
var xhr;
|
|
beforeEach(function() {
|
|
xhr = context.responseCallback.calls.mostRecent().args[0];
|
|
});
|
|
|
|
it("should have the expected status code", function() {
|
|
expect(xhr.status).toEqual(context.status);
|
|
});
|
|
|
|
it("should have the expected content type", function() {
|
|
expect(xhr.getResponseHeader('Content-Type')).toEqual(context.contentType);
|
|
});
|
|
|
|
it("should have the expected xhr2 response", function() {
|
|
var expected = context.response || xhr.responseType === 'json' ? JSON.parse(xhr.responseText) : xhr.responseText;
|
|
expect(xhr.response).toEqual(expected);
|
|
});
|
|
|
|
it("should have the expected response text", function() {
|
|
expect(xhr.responseText).toEqual(context.responseText);
|
|
});
|
|
|
|
it("should have the expected status text", function() {
|
|
expect(xhr.statusText).toEqual(context.statusText);
|
|
});
|
|
|
|
it("should have the expected response URL", function() {
|
|
expect(xhr.responseURL).toEqual(context.responseURL || null);
|
|
});
|
|
});
|
|
}
|