Allow error and timeout using RequestStub

Closes #111
This commit is contained in:
Martin Jantosovic 2015-06-01 19:12:13 +02:00
parent e1d61c4143
commit 732f91f5f1
4 changed files with 110 additions and 17 deletions

View File

@ -1,6 +1,6 @@
/* /*
Jasmine-Ajax - v3.1.0: a set of helpers for testing AJAX requests under the Jasmine Jasmine-Ajax - v3.1.1: a set of helpers for testing AJAX requests under the Jasmine
BDD framework for JavaScript. BDD framework for JavaScript.
http://github.com/jasmine/jasmine-ajax http://github.com/jasmine/jasmine-ajax
@ -68,6 +68,10 @@ getJasmineRequireObj().AjaxEventBus = function() {
return -1; return -1;
} }
function slice(array, start) {
return Array.prototype.slice.call(array, start);
}
EventBus.prototype.addEventListener = function(event, callback) { EventBus.prototype.addEventListener = function(event, callback) {
ensureEvent(this.eventList, event).push(callback); ensureEvent(this.eventList, event).push(callback);
}; };
@ -81,11 +85,13 @@ getJasmineRequireObj().AjaxEventBus = function() {
}; };
EventBus.prototype.trigger = function(event) { EventBus.prototype.trigger = function(event) {
// Arguments specified after event name need to be propagated
var args = slice(arguments, 1);
var eventListeners = this.eventList[event]; var eventListeners = this.eventList[event];
if(eventListeners){ if(eventListeners){
for(var i = 0; i < eventListeners.length; i++){ for(var i = 0; i < eventListeners.length; i++){
eventListeners[i](); eventListeners[i].apply(this, args);
} }
} }
}; };
@ -117,12 +123,13 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
function wrapProgressEvent(xhr, eventName) { function wrapProgressEvent(xhr, eventName) {
return function() { return function() {
if (xhr[eventName]) { if (xhr[eventName]) {
xhr[eventName](); xhr[eventName].apply(xhr, arguments);
} }
}; };
} }
function initializeEvents(xhr) { function initializeEvents(xhr) {
xhr.eventBus.addEventListener('readystatechange', wrapProgressEvent(xhr, 'onreadystatechange'));
xhr.eventBus.addEventListener('loadstart', wrapProgressEvent(xhr, 'onloadstart')); xhr.eventBus.addEventListener('loadstart', wrapProgressEvent(xhr, 'onloadstart'));
xhr.eventBus.addEventListener('load', wrapProgressEvent(xhr, 'onload')); xhr.eventBus.addEventListener('load', wrapProgressEvent(xhr, 'onload'));
xhr.eventBus.addEventListener('loadend', wrapProgressEvent(xhr, 'onloadend')); xhr.eventBus.addEventListener('loadend', wrapProgressEvent(xhr, 'onloadend'));
@ -211,7 +218,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
this.username = arguments[3]; this.username = arguments[3];
this.password = arguments[4]; this.password = arguments[4];
this.readyState = 1; this.readyState = 1;
this.onreadystatechange(); this.eventBus.trigger('readystatechange');
}, },
setRequestHeader: function(header, value) { setRequestHeader: function(header, value) {
@ -230,7 +237,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
this.readyState = 0; this.readyState = 0;
this.status = 0; this.status = 0;
this.statusText = "abort"; this.statusText = "abort";
this.onreadystatechange(); this.eventBus.trigger('readystatechange');
this.eventBus.trigger('progress'); this.eventBus.trigger('progress');
this.eventBus.trigger('abort'); this.eventBus.trigger('abort');
this.eventBus.trigger('loadend'); this.eventBus.trigger('loadend');
@ -245,9 +252,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
onload: null, onload: null,
ontimeout: null, ontimeout: null,
onloadend: null, onloadend: null,
onreadystatechange: null,
onreadystatechange: function(isTimeout) {
},
addEventListener: function() { addEventListener: function() {
this.eventBus.addEventListener.apply(this.eventBus, arguments); this.eventBus.addEventListener.apply(this.eventBus, arguments);
@ -261,13 +266,17 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
send: function(data) { send: function(data) {
this.params = data; this.params = data;
this.readyState = 2;
this.eventBus.trigger('loadstart'); this.eventBus.trigger('loadstart');
this.onreadystatechange();
var stub = stubTracker.findStub(this.url, data, this.method); var stub = stubTracker.findStub(this.url, data, this.method);
if (stub) { if (stub) {
this.respondWith(stub); if (stub.isReturn()) {
this.respondWith(stub);
} else if (stub.isError()) {
this.responseError();
} else if (stub.isTimeout()) {
this.responseTimeout();
}
} }
}, },
@ -334,12 +343,16 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
if (this.readyState === 4) { if (this.readyState === 4) {
throw new Error("FakeXMLHttpRequest already completed"); throw new Error("FakeXMLHttpRequest already completed");
} }
this.status = response.status; this.status = response.status;
this.statusText = response.statusText || ""; this.statusText = response.statusText || "";
this.responseHeaders = normalizeHeaders(response.responseHeaders, response.contentType);
this.readyState = 2;
this.eventBus.trigger('readystatechange');
this.responseText = response.responseText || ""; this.responseText = response.responseText || "";
this.responseType = response.responseType || ""; this.responseType = response.responseType || "";
this.readyState = 4; this.readyState = 4;
this.responseHeaders = normalizeHeaders(response.responseHeaders, response.contentType);
this.responseXML = getResponseXml(response.responseText, this.getResponseHeader('content-type') || ''); this.responseXML = getResponseXml(response.responseText, this.getResponseHeader('content-type') || '');
if (this.responseXML) { if (this.responseXML) {
this.responseType = 'document'; this.responseType = 'document';
@ -351,7 +364,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
this.response = this.responseValue(); this.response = this.responseValue();
} }
this.onreadystatechange(); this.eventBus.trigger('readystatechange');
this.eventBus.trigger('progress'); this.eventBus.trigger('progress');
this.eventBus.trigger('load'); this.eventBus.trigger('load');
this.eventBus.trigger('loadend'); this.eventBus.trigger('loadend');
@ -363,7 +376,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
} }
this.readyState = 4; this.readyState = 4;
jasmine.clock().tick(30000); jasmine.clock().tick(30000);
this.onreadystatechange('timeout'); this.eventBus.trigger('readystatechange', 'timeout');
this.eventBus.trigger('progress'); this.eventBus.trigger('progress');
this.eventBus.trigger('timeout'); this.eventBus.trigger('timeout');
this.eventBus.trigger('loadend'); this.eventBus.trigger('loadend');
@ -374,7 +387,7 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
throw new Error("FakeXMLHttpRequest already completed"); throw new Error("FakeXMLHttpRequest already completed");
} }
this.readyState = 4; this.readyState = 4;
this.onreadystatechange(); this.eventBus.trigger('readystatechange');
this.eventBus.trigger('progress'); this.eventBus.trigger('progress');
this.eventBus.trigger('error'); this.eventBus.trigger('error');
this.eventBus.trigger('loadend'); this.eventBus.trigger('loadend');
@ -495,6 +508,10 @@ getJasmineRequireObj().AjaxParamParser = function() {
}; };
getJasmineRequireObj().AjaxRequestStub = function() { getJasmineRequireObj().AjaxRequestStub = function() {
var RETURN = 0,
ERROR = 1,
TIMEOUT = 2;
function RequestStub(url, stubData, method) { function RequestStub(url, stubData, method) {
var normalizeQuery = function(query) { var normalizeQuery = function(query) {
return query ? query.split('&').sort().join('&') : undefined; return query ? query.split('&').sort().join('&') : undefined;
@ -513,6 +530,7 @@ getJasmineRequireObj().AjaxRequestStub = function() {
this.method = method; this.method = method;
this.andReturn = function(options) { this.andReturn = function(options) {
this.action = RETURN;
this.status = options.status || 200; this.status = options.status || 200;
this.contentType = options.contentType; this.contentType = options.contentType;
@ -521,6 +539,26 @@ getJasmineRequireObj().AjaxRequestStub = function() {
this.responseHeaders = options.responseHeaders; this.responseHeaders = options.responseHeaders;
}; };
this.isReturn = function() {
return this.action === RETURN;
};
this.andError = function() {
this.action = ERROR;
};
this.isError = function() {
return this.action === ERROR;
};
this.andTimeout = function() {
this.action = TIMEOUT;
};
this.isTimeout = function() {
return this.action === TIMEOUT;
};
this.matches = function(fullUrl, data, method) { this.matches = function(fullUrl, data, method) {
var matches = false; var matches = false;
fullUrl = fullUrl.toString(); fullUrl = fullUrl.toString();

View File

@ -1,5 +1,5 @@
describe("Webmock style mocking", function() { describe("Webmock style mocking", function() {
var successSpy, errorSpy, response, fakeGlobal, mockAjax; var successSpy, errorSpy, timeoutSpy, response, fakeGlobal, mockAjax;
var sendRequest = function(fakeGlobal, url, method) { var sendRequest = function(fakeGlobal, url, method) {
url = url || "http://example.com/someApi"; url = url || "http://example.com/someApi";
@ -12,17 +12,29 @@ describe("Webmock style mocking", function() {
} }
}; };
xhr.onerror = function() {
errorSpy();
};
xhr.ontimeout = function() {
timeoutSpy();
};
xhr.open(method, url); xhr.open(method, url);
xhr.send(); xhr.send();
}; };
beforeEach(function() { beforeEach(function() {
successSpy = jasmine.createSpy('success'); successSpy = jasmine.createSpy('success');
errorSpy = jasmine.createSpy('error');
timeoutSpy = jasmine.createSpy('timeout');
fakeGlobal = {XMLHttpRequest: jasmine.createSpy('realXMLHttpRequest')}; fakeGlobal = {XMLHttpRequest: jasmine.createSpy('realXMLHttpRequest')};
mockAjax = new window.MockAjax(fakeGlobal); mockAjax = new window.MockAjax(fakeGlobal);
mockAjax.install(); mockAjax.install();
mockAjax.stubRequest("http://example.com/someApi").andReturn({responseText: "hi!"}); mockAjax.stubRequest("http://example.com/someApi").andReturn({responseText: "hi!"});
mockAjax.stubRequest("http://example.com/someErrorApi").andError();
mockAjax.stubRequest("http://example.com/someTimeoutApi").andTimeout();
}); });
it("allows a url to be setup as a stub", function() { it("allows a url to be setup as a stub", function() {
@ -30,6 +42,18 @@ describe("Webmock style mocking", function() {
expect(successSpy).toHaveBeenCalled(); expect(successSpy).toHaveBeenCalled();
}); });
it("allows a url to be setup as a stub which trigger error", function() {
sendRequest(fakeGlobal, "http://example.com/someErrorApi");
expect(errorSpy).toHaveBeenCalled();
});
it("allows a url to be setup as a stub which timeouts", function() {
jasmine.clock().install();
sendRequest(fakeGlobal, "http://example.com/someTimeoutApi");
expect(timeoutSpy).toHaveBeenCalled();
jasmine.clock().uninstall();
});
it("should allow you to clear all the ajax stubs", function() { it("should allow you to clear all the ajax stubs", function() {
mockAjax.stubs.reset(); mockAjax.stubs.reset();
sendRequest(fakeGlobal); sendRequest(fakeGlobal);

View File

@ -168,7 +168,13 @@ getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
var stub = stubTracker.findStub(this.url, data, this.method); var stub = stubTracker.findStub(this.url, data, this.method);
if (stub) { if (stub) {
this.respondWith(stub); if (stub.isReturn()) {
this.respondWith(stub);
} else if (stub.isError()) {
this.responseError();
} else if (stub.isTimeout()) {
this.responseTimeout();
}
} }
}, },

View File

@ -1,4 +1,8 @@
getJasmineRequireObj().AjaxRequestStub = function() { getJasmineRequireObj().AjaxRequestStub = function() {
var RETURN = 0,
ERROR = 1,
TIMEOUT = 2;
function RequestStub(url, stubData, method) { function RequestStub(url, stubData, method) {
var normalizeQuery = function(query) { var normalizeQuery = function(query) {
return query ? query.split('&').sort().join('&') : undefined; return query ? query.split('&').sort().join('&') : undefined;
@ -17,6 +21,7 @@ getJasmineRequireObj().AjaxRequestStub = function() {
this.method = method; this.method = method;
this.andReturn = function(options) { this.andReturn = function(options) {
this.action = RETURN;
this.status = options.status || 200; this.status = options.status || 200;
this.contentType = options.contentType; this.contentType = options.contentType;
@ -25,6 +30,26 @@ getJasmineRequireObj().AjaxRequestStub = function() {
this.responseHeaders = options.responseHeaders; this.responseHeaders = options.responseHeaders;
}; };
this.isReturn = function() {
return this.action === RETURN;
};
this.andError = function() {
this.action = ERROR;
};
this.isError = function() {
return this.action === ERROR;
};
this.andTimeout = function() {
this.action = TIMEOUT;
};
this.isTimeout = function() {
return this.action === TIMEOUT;
};
this.matches = function(fullUrl, data, method) { this.matches = function(fullUrl, data, method) {
var matches = false; var matches = false;
fullUrl = fullUrl.toString(); fullUrl = fullUrl.toString();