Add responseURL support

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
This commit is contained in:
Joey Parrish 2016-01-12 12:24:19 -08:00
parent 920f8e7771
commit 4df24d7e81
5 changed files with 70 additions and 2 deletions

View File

@ -269,7 +269,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
return null;
}
var iePropertiesThatCannotBeCopied = ['responseBody', 'responseText', 'responseXML', 'status', 'statusText', 'responseTimeout'];
var iePropertiesThatCannotBeCopied = ['responseBody', 'responseText', 'responseXML', 'status', 'statusText', 'responseTimeout', 'responseURL'];
extend(FakeXMLHttpRequest.prototype, new global.XMLHttpRequest(), iePropertiesThatCannotBeCopied);
extend(FakeXMLHttpRequest.prototype, {
open: function() {
@ -380,6 +380,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
responseText: null,
response: null,
responseType: null,
responseURL: null,
responseValue: function() {
switch(this.responseType) {
@ -412,6 +413,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
this.responseText = response.responseText || "";
this.responseType = response.responseType || "";
this.responseURL = response.responseURL || null;
this.readyState = 4;
this.responseXML = getResponseXml(response.responseText, this.getResponseHeader('content-type') || '');
if (this.responseXML) {
@ -600,6 +602,7 @@ getJasmineRequireObj().AjaxRequestStub = function() {
this.response = options.response;
this.responseText = options.responseText;
this.responseHeaders = options.responseHeaders;
this.responseURL = options.responseURL;
};
this.isReturn = function() {

View File

@ -489,6 +489,26 @@ describe('FakeRequest', function() {
expect(request.responseText).toBe('');
});
it('saves responseURL', function() {
var request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200, responseText: 'foobar', responseURL: 'foo.bar/redirect' });
expect(request.responseURL).toBe('foo.bar/redirect');
});
it('defaults responseText if none is given', function() {
var request = new this.FakeRequest();
request.open();
request.send();
request.respondWith({ status: 200 });
expect(request.responseURL).toBe(null);
});
it('retrieves individual response headers', function() {
var request = new this.FakeRequest();
request.open();

View File

@ -143,6 +143,7 @@ describe("Jasmine Mock Ajax (for toplevel)", function() {
sharedContext.contentType = response.contentType;
sharedContext.responseText = response.responseText;
sharedContext.responseType = response.responseType;
sharedContext.responseURL = response.responseURL;
});
it("should call the success handler", function() {
@ -179,6 +180,7 @@ describe("Jasmine Mock Ajax (for toplevel)", function() {
sharedContext.contentType = responseObject.contentType;
sharedContext.responseText = responseObject.responseText;
sharedContext.responseType = responseObject.responseType;
sharedContext.responseURL = responseObject.responseURL;
response = success.calls.mostRecent().args[2];
});
@ -225,6 +227,7 @@ describe("Jasmine Mock Ajax (for toplevel)", function() {
sharedContext.contentType = responseObject.contentType;
sharedContext.responseText = responseObject.responseText;
sharedContext.responseType = responseObject.responseType;
sharedContext.responseURL = responseObject.responseURL;
response = success.calls.mostRecent().args[2];
});
@ -239,6 +242,33 @@ describe("Jasmine Mock Ajax (for toplevel)", function() {
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();
@ -317,6 +347,7 @@ describe("Jasmine Mock Ajax (for toplevel)", function() {
sharedContext.contentType = "application/json";
sharedContext.responseType = response.responseType;
sharedContext.responseText = response.responseText;
sharedContext.responseURL = response.responseURL;
});
it("should call the success handler", function() {
@ -352,6 +383,7 @@ describe("Jasmine Mock Ajax (for toplevel)", function() {
sharedContext.contentType = 'application/json';
sharedContext.responseText = response.responseText;
sharedContext.responseType = response.responseType;
sharedContext.responseURL = response.responseURL;
});
it("should call the success handler", function() {
@ -388,6 +420,7 @@ describe("Jasmine Mock Ajax (for toplevel)", function() {
sharedContext.contentType = response.contentType;
sharedContext.responseText = response.responseText;
sharedContext.responseType = response.responseType;
sharedContext.responseURL = response.responseURL;
});
it("should not call the success handler", function() {
@ -425,6 +458,7 @@ describe("Jasmine Mock Ajax (for toplevel)", function() {
sharedContext.contentType = response.contentType;
sharedContext.responseText = response.responseText;
sharedContext.responseType = response.responseType;
sharedContext.responseURL = response.responseURL;
});
afterEach(function() {
@ -473,6 +507,10 @@ function sharedAjaxResponseBehaviorForZepto_Success(context) {
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);
});
});
}
@ -503,5 +541,9 @@ function sharedAjaxResponseBehaviorForZepto_Failure(context) {
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);
});
});
}

View File

@ -107,7 +107,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
return null;
}
var iePropertiesThatCannotBeCopied = ['responseBody', 'responseText', 'responseXML', 'status', 'statusText', 'responseTimeout'];
var iePropertiesThatCannotBeCopied = ['responseBody', 'responseText', 'responseXML', 'status', 'statusText', 'responseTimeout', 'responseURL'];
extend(FakeXMLHttpRequest.prototype, new global.XMLHttpRequest(), iePropertiesThatCannotBeCopied);
extend(FakeXMLHttpRequest.prototype, {
open: function() {
@ -218,6 +218,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
responseText: null,
response: null,
responseType: null,
responseURL: null,
responseValue: function() {
switch(this.responseType) {
@ -250,6 +251,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
this.responseText = response.responseText || "";
this.responseType = response.responseType || "";
this.responseURL = response.responseURL || null;
this.readyState = 4;
this.responseXML = getResponseXml(response.responseText, this.getResponseHeader('content-type') || '');
if (this.responseXML) {

View File

@ -28,6 +28,7 @@ getJasmineRequireObj().AjaxRequestStub = function() {
this.response = options.response;
this.responseText = options.responseText;
this.responseHeaders = options.responseHeaders;
this.responseURL = options.responseURL;
};
this.isReturn = function() {