An attempt to explore how error handling in ExtJS AJAX requests work. Use ExtJS, Jasmine
/** | |
* An attempt to explore how error handling in ExtJS AJAX requests work, | |
* in particular looking at ways to re-try requests and cope with | |
* both the conventional 'success/fail/callback' style and this | |
* new-fangled 'then/always/otherwise' promise approach. | |
*/ | |
describe("Twasink.spike.AjaxErrorHandlingSpikeSpec", function() { | |
var successHandler; | |
var failHandler; | |
var alwaysHandler; | |
beforeEach(function() { | |
successHandler = jasmine.createSpy("success"); | |
failHandler = jasmine.createSpy("fail"); | |
alwaysHandler = jasmine.createSpy("always"); | |
}) | |
beforeEach(function() { | |
jasmine.Ajax.install(); | |
}); | |
afterEach(function() { | |
jasmine.Ajax.uninstall(); | |
}); | |
beforeAll(function() { | |
Ext.Ajax.on('requestexception', 'listener', ajaxErrorHandler); | |
}) | |
beforeEach(function() { | |
ajaxErrorHandler.called = false; | |
}) | |
afterEach(function() { | |
ajaxErrorHandler.originalOptions = null; | |
ajaxErrorHandler.originalDeferred = null; | |
}) | |
// Each approach for making an AJAX call differs in calling style, but should all be | |
// treated the same. To facilitate this, I'm using a little meta-programming here, | |
// defining a test suit as an object that I can call into multiple times, | |
// each time with a new ajax handler. | |
function runAjaxTests(title, ajaxCall) { | |
describe(title, function() { | |
function makeAjaxCall() { | |
ajaxCall(); | |
expect(jasmine.Ajax.requests.mostRecent().url).toBe('http://example.org/'); | |
} | |
// Prove I know how to make a normal call that works. | |
it("successful call example", function(done) { | |
makeAjaxCall(); | |
jasmine.Ajax.requests.mostRecent().respondWith({ | |
status: 200 | |
}); | |
Ext.asap(function() { | |
expect(ajaxErrorHandler.called).not.toBeTruthy(); | |
expect(successHandler.calls.count()).toEqual(1); | |
expect(failHandler.calls.count()).toEqual(0); | |
expect(alwaysHandler.calls.count()).toEqual(1); | |
done() | |
}) | |
}) | |
// Prove I know how to make a normal call that fails. | |
it("unhandled error example", function(done) { | |
makeAjaxCall(); | |
jasmine.Ajax.requests.mostRecent().respondWith({ | |
status: 500 | |
}); | |
Ext.asap(function() { | |
expect(ajaxErrorHandler.called).toBeTruthy(); | |
expect(successHandler.calls.count()).toEqual(0); | |
expect(failHandler.calls.count()).toEqual(1); | |
expect(alwaysHandler.calls.count()).toEqual(1); | |
done(); | |
}) | |
}) | |
// Prove I know how to make a normal call that fails and is retried. | |
it("handled error", function(done) { | |
makeAjaxCall(); | |
expect(ajaxErrorHandler.called).not.toBeTruthy(); | |
expect(jasmine.Ajax.requests.count()).toEqual(1); | |
jasmine.Ajax.requests.mostRecent().respondWith({ | |
status: 401 | |
}); | |
expect(ajaxErrorHandler.called).toBeTruthy(); | |
// Nothing should have been called. | |
expect(successHandler.calls.count()).toEqual(0); | |
expect(failHandler.calls.count()).toEqual(0); | |
expect(alwaysHandler.calls.count()).toEqual(0); | |
ajaxErrorHandler.retryCall(); | |
expect(jasmine.Ajax.requests.count()).toEqual(2); | |
jasmine.Ajax.requests.mostRecent().respondWith({ | |
status: 200 | |
}); | |
Ext.asap(function() { | |
expect(successHandler.calls.count()).toEqual(1); | |
expect(failHandler.calls.count()).toEqual(0); | |
expect(alwaysHandler.calls.count()).toEqual(1); | |
done(); | |
}) | |
}) | |
}) | |
} | |
// And here's our attempt at handling the error. All we'll do here is make a new Ajax call and do our | |
// best to cancel the old one. | |
var ajaxErrorHandler = { | |
listener: function(connection, response, options) { | |
this.called = true; | |
// Start by doing nothing, and seeing the red bar | |
var originalRequest = response.request; | |
switch(response.status) { | |
case 401: | |
this.handleTheError(originalRequest, response, options); | |
return; | |
default: | |
// do nothing by default | |
return | |
} | |
}, | |
handleTheError: function(request, response, options) { | |
console.log("Handling 401 error"); | |
// capture the details. | |
this.originalOptions = Ext.clone(options); | |
this.originalDeferred = request.deferred; | |
// stop the original error. | |
options.failure = null; | |
// stop the callback, if any | |
options.callback = null; | |
request.deferred = null; | |
}, | |
retryCall: function() { | |
var request = Ext.Ajax.request(this.originalOptions); | |
request.deferred = this.originalDeferred; | |
} | |
} | |
runAjaxTests("Conventional Ajax Approach", function() { | |
Ext.Ajax.request({ | |
url: 'http://example.org/', | |
disableCaching: false, | |
success: successHandler, | |
failure: failHandler, | |
callback: alwaysHandler | |
}) | |
}); | |
runAjaxTests("Promise-style Approach 1", function() { | |
Ext.Ajax.request({ | |
url: 'http://example.org/', | |
disableCaching: false, | |
}) | |
.then(successHandler, failHandler) | |
.always(alwaysHandler) | |
}) | |
runAjaxTests("Promise-style Approach 2", function() { | |
Ext.Ajax.request({ | |
url: 'http://example.org/', | |
disableCaching: false, | |
}) | |
.then(successHandler) | |
.otherwise(failHandler) | |
.always(alwaysHandler) | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment