From 05e2c3196c857402a9aa93837b565e0a2736af23 Mon Sep 17 00:00:00 2001 From: Di Peng Date: Tue, 2 Aug 2011 16:44:25 -0700 Subject: [PATCH] feat($browser): JSONP error handling since we don't know if the error was due to a client error (4xx) or server error (5xx), we leave the status code as undefined. --- src/Browser.js | 26 ++++++++++--- test/BrowserSpecs.js | 90 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 93 insertions(+), 23 deletions(-) diff --git a/src/Browser.js b/src/Browser.js index 815b6b2419e6..a53687d5a515 100644 --- a/src/Browser.js +++ b/src/Browser.js @@ -100,12 +100,19 @@ function Browser(window, document, body, XHR, $log) { outstandingRequestCount ++; if (lowercase(method) == 'json') { var callbackId = ("angular_" + Math.random() + '_' + (idCounter++)).replace(/\d\./, ''); - var script = self.addJs(url.replace('JSON_CALLBACK', callbackId)); - window[callbackId] = function(data){ + window[callbackId] = function(data) { + window[callbackId].data = data; + }; + + var script = self.addJs(url.replace('JSON_CALLBACK', callbackId), null, function() { + if (window[callbackId].data) { + completeOutstandingRequest(callback, 200, window[callbackId].data); + } else { + completeOutstandingRequest(callback); + } delete window[callbackId]; body[0].removeChild(script); - completeOutstandingRequest(callback, 200, data); - }; + }); } else { var xhr = new XHR(); xhr.open(method, url, true); @@ -452,7 +459,7 @@ function Browser(window, document, body, XHR, $log) { * @description * Adds a script tag to the head. */ - self.addJs = function(url, domId) { + self.addJs = function(url, domId, done) { // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: // - fetches local scripts via XHR and evals them // - adds and immediately removes script elements from the document @@ -465,6 +472,15 @@ function Browser(window, document, body, XHR, $log) { script.type = 'text/javascript'; script.src = url; if (domId) script.id = domId; + + if (msie) { + script.onreadystatechange = function() { + /loaded|complete/.test(script.readyState) && done && done(); + } + } else { + if (done) script.onload = script.onerror = done; + } + body[0].appendChild(script); return script; diff --git a/test/BrowserSpecs.js b/test/BrowserSpecs.js index ebaf0651da26..92e4e5017c9f 100644 --- a/test/BrowserSpecs.js +++ b/test/BrowserSpecs.js @@ -87,27 +87,81 @@ describe('browser', function(){ describe('xhr', function(){ describe('JSON', function(){ - it('should add script tag for request', function() { - var callback = jasmine.createSpy('callback'); - var log = ""; - browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', null, function(code, data){ - log += code + ':' + data + ';'; - }); - browser.notifyWhenNoOutstandingRequests(callback); - expect(callback).not.toHaveBeenCalled(); - expect(scripts.length).toEqual(1); - var script = scripts[0]; - var url = script.src.split('?cb='); - expect(url[0]).toEqual('http://example.org/path'); - expect(typeof fakeWindow[url[1]]).toEqual($function); - fakeWindow[url[1]]('data'); - expect(callback).toHaveBeenCalled(); - expect(log).toEqual('200:data;'); - expect(scripts).toEqual(removedScripts); - expect(fakeWindow[url[1]]).toBeUndefined(); + var log; + + function callback(code, data) { + log += code + ':' + data + ';'; + } + + beforeEach(function() { + log = ""; }); + + + // We don't have unit tests for IE because script.readyState is readOnly. + // Instead we run e2e tests on all browsers - see e2e for $xhr. + if (!msie) { + + it('should add script tag for JSONP request', function() { + var notify = jasmine.createSpy('notify'); + browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', null, callback); + browser.notifyWhenNoOutstandingRequests(notify); + expect(notify).not.toHaveBeenCalled(); + expect(scripts.length).toEqual(1); + var script = scripts[0]; + var url = script.src.split('?cb='); + expect(url[0]).toEqual('http://example.org/path'); + expect(typeof fakeWindow[url[1]]).toEqual($function); + fakeWindow[url[1]]('data'); + script.onload(); + + expect(notify).toHaveBeenCalled(); + expect(log).toEqual('200:data;'); + expect(scripts).toEqual(removedScripts); + expect(fakeWindow[url[1]]).toBeUndefined(); + }); + + + it('should call callback when script fails to load', function() { + browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', null, callback); + var script = scripts[0]; + expect(typeof script.onload).toBe($function); + expect(typeof script.onerror).toBe($function); + script.onerror(); + + expect(log).toEqual('undefined:undefined;'); + }); + + + it('should update the outstandingRequests counter for successful requests', function() { + var notify = jasmine.createSpy('notify'); + browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', null, callback); + browser.notifyWhenNoOutstandingRequests(notify); + expect(notify).not.toHaveBeenCalled(); + + var script = scripts[0]; + var url = script.src.split('?cb='); + fakeWindow[url[1]]('data'); + script.onload(); + + expect(notify).toHaveBeenCalled(); + }); + + + it('should update the outstandingRequests counter for failed requests', function() { + var notify = jasmine.createSpy('notify'); + browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', null, callback); + browser.notifyWhenNoOutstandingRequests(notify); + expect(notify).not.toHaveBeenCalled(); + + scripts[0].onerror(); + + expect(notify).toHaveBeenCalled(); + }); + } }); + it('should normalize IE\'s 1223 status code into 204', function() { var callback = jasmine.createSpy('XHR');