diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/angular-mocks.js | 180 | ||||
| -rw-r--r-- | src/service/browser.js | 104 | ||||
| -rw-r--r-- | src/service/httpBackend.js | 84 |
3 files changed, 107 insertions, 261 deletions
diff --git a/src/angular-mocks.js b/src/angular-mocks.js index 2aabb96d..00541c8f 100644 --- a/src/angular-mocks.js +++ b/src/angular-mocks.js @@ -57,6 +57,10 @@ angular.module.ngMock.$Browser = function() { self.$$lastUrl = self.$$url; // used by url polling fn self.pollFns = []; + // TODO(vojta): remove this temporary api + self.$$completeOutstandingRequest = noop; + self.$$incOutstandingRequestCount = noop; + // register url polling fn @@ -73,165 +77,6 @@ angular.module.ngMock.$Browser = function() { return listener; }; - - /** - * @ngdoc method - * @name angular.module.ngMock.$browser#xhr - * @methodOf angular.module.ngMock.$browser - * - * @description - * Generic method for training browser to expect a request in a test and respond to it. - * - * See also convenience methods for browser training: - * - * - {@link #xhr.expectGET} - * - {@link #xhr.expectPOST} - * - {@link #xhr.expectPUT} - * - {@link #xhr.expectDELETE} - * - {@link #xhr.expectJSON} - * - * To flush pending requests in tests use - * {@link #xhr.flush}. - * - * @param {string} method Expected HTTP method. - * @param {string} url Url path for which a request is expected. - * @param {(object|string)=} data Expected body of the (POST) HTTP request. - * @param {function(number, *)} callback Callback to call when response is flushed. - * @param {object} headers Key-value pairs of expected headers. - * @returns {object} Response configuration object. You can call its `respond()` method to - * configure what should the browser mock return when the response is - * {@link #xhr.flush flushed}. - */ - self.xhr = function(method, url, data, callback, headers) { - headers = headers || {}; - if (data && angular.isObject(data)) data = angular.toJson(data); - if (data && angular.isString(data)) url += "|" + data; - var expect = expectations[method] || {}; - var expectation = expect[url]; - if (!expectation) { - throw new Error("Unexpected request for method '" + method + "' and url '" + url + "'."); - } - requests.push(function() { - angular.forEach(expectation.headers, function(value, key){ - if (headers[key] !== value) { - throw new Error("Missing HTTP request header: " + key + ": " + value); - } - }); - callback(expectation.code, expectation.response); - }); - // TODO(vojta): return mock request object - }; - self.xhr.expectations = expectations; - self.xhr.requests = requests; - self.xhr.expect = function(method, url, data, headers) { - if (data && angular.isObject(data)) data = angular.toJson(data); - if (data && angular.isString(data)) url += "|" + data; - var expect = expectations[method] || (expectations[method] = {}); - return { - respond: function(code, response) { - if (!angular.isNumber(code)) { - response = code; - code = 200; - } - expect[url] = {code:code, response:response, headers: headers || {}}; - } - }; - }; - - /** - * @ngdoc method - * @name angular.module.ngMock.$browser#xhr.expectGET - * @methodOf angular.module.ngMock.$browser - * - * @description - * Trains browser to expect a `GET` request and respond to it. - * - * @param {string} url Url path for which a request is expected. - * @returns {object} Response configuration object. You can call its `respond()` method to - * configure what should the browser mock return when the response is - * {@link angular.module.ngMock.$browser#xhr.flush flushed}. - */ - self.xhr.expectGET = angular.bind(self, self.xhr.expect, 'GET'); - - /** - * @ngdoc method - * @name angular.module.ngMock.$browser#xhr.expectPOST - * @methodOf angular.module.ngMock.$browser - * - * @description - * Trains browser to expect a `POST` request and respond to it. - * - * @param {string} url Url path for which a request is expected. - * @returns {object} Response configuration object. You can call its `respond()` method to - * configure what should the browser mock return when the response is - * {@link angular.module.ngMock.$browser#xhr.flush flushed}. - */ - self.xhr.expectPOST = angular.bind(self, self.xhr.expect, 'POST'); - - /** - * @ngdoc method - * @name angular.module.ngMock.$browser#xhr.expectDELETE - * @methodOf angular.module.ngMock.$browser - * - * @description - * Trains browser to expect a `DELETE` request and respond to it. - * - * @param {string} url Url path for which a request is expected. - * @returns {object} Response configuration object. You can call its `respond()` method to - * configure what should the browser mock return when the response is - * {@link angular.module.ngMock.$browser#xhr.flush flushed}. - */ - self.xhr.expectDELETE = angular.bind(self, self.xhr.expect, 'DELETE'); - - /** - * @ngdoc method - * @name angular.module.ngMock.$browser#xhr.expectPUT - * @methodOf angular.module.ngMock.$browser - * - * @description - * Trains browser to expect a `PUT` request and respond to it. - * - * @param {string} url Url path for which a request is expected. - * @returns {object} Response configuration object. You can call its `respond()` method to - * configure what should the browser mock return when the response is - * {@link angular.module.ngMock.$browser#xhr.flush flushed}. - */ - self.xhr.expectPUT = angular.bind(self, self.xhr.expect, 'PUT'); - - /** - * @ngdoc method - * @name angular.module.ngMock.$browser#xhr.expectJSON - * @methodOf angular.module.ngMock.$browser - * - * @description - * Trains browser to expect a `JSON` request and respond to it. - * - * @param {string} url Url path for which a request is expected. - * @returns {object} Response configuration object. You can call its `respond()` method to - * configure what should the browser mock return when the response is - * {@link angular.module.ngMock.$browser#xhr.flush flushed}. - */ - self.xhr.expectJSON = angular.bind(self, self.xhr.expect, 'JSON'); - - /** - * @ngdoc method - * @name angular.module.ngMock.$browser#xhr.flush - * @methodOf angular.module.ngMock.$browser - * - * @description - * Flushes all pending requests and executes xhr callbacks with the trained response as the - * argument. - */ - self.xhr.flush = function() { - if (requests.length == 0) { - throw new Error("No xhr requests to be flushed!"); - } - - while(requests.length) { - requests.pop()(); - } - }; - self.cookieHash = {}; self.lastCookieHash = {}; self.deferredFns = []; @@ -871,9 +716,24 @@ function MockHttpExpectation(method, url, data, headers) { function MockXhr() { - // hack for testing $http + // hack for testing $http, $httpBackend MockXhr.$$lastInstance = this; + this.open = function(method, url, async) { + this.$$method = method; + this.$$url = url; + this.$$async = async; + this.$$headers = {}; + }; + + this.send = function(data) { + this.$$data = data; + }; + + this.setRequestHeader = function(key, value) { + this.$$headers[key] = value; + }; + this.getResponseHeader = function(name) { return this.$$headers[name]; }; diff --git a/src/service/browser.js b/src/service/browser.js index 74bea44c..97e9cf3e 100644 --- a/src/service/browser.js +++ b/src/service/browser.js @@ -1,16 +1,5 @@ 'use strict'; -////////////////////////////// -// Browser -////////////////////////////// -var XHR = window.XMLHttpRequest || function() { - try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} - try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {} - try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {} - throw new Error("This browser does not support XMLHttpRequest."); -}; - - /** * @ngdoc object * @name angular.module.ng.$browser @@ -33,7 +22,7 @@ var XHR = window.XMLHttpRequest || function() { * @param {object} $log console.log or an object with the same interface. * @param {object} $sniffer $sniffer service */ -function Browser(window, document, body, XHR, $log, $sniffer) { +function Browser(window, document, body, $log, $sniffer) { var self = this, rawDocument = document[0], location = window.location, @@ -44,13 +33,12 @@ function Browser(window, document, body, XHR, $log, $sniffer) { self.isMock = false; - ////////////////////////////////////////////////////////////// - // XHR API - ////////////////////////////////////////////////////////////// - var idCounter = 0; var outstandingRequestCount = 0; var outstandingRequestCallbacks = []; + // TODO(vojta): remove this temporary api + self.$$completeOutstandingRequest = completeOutstandingRequest; + self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; /** * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` @@ -73,88 +61,6 @@ function Browser(window, document, body, XHR, $log, $sniffer) { } } - // normalize IE bug (http://bugs.jquery.com/ticket/1450) - function fixStatus(status) { - return status == 1223 ? 204 : status; - } - - /** - * @ngdoc method - * @name angular.module.ng.$browser#xhr - * @methodOf angular.module.ng.$browser - * - * @param {string} method Requested method (get|post|put|delete|head|json) - * @param {string} url Requested url - * @param {?string} post Post data to send (null if nothing to post) - * @param {function(number, string)} callback Function that will be called on response - * @param {object=} header additional HTTP headers to send with XHR. - * Standard headers are: - * <ul> - * <li><tt>Content-Type</tt>: <tt>application/x-www-form-urlencoded</tt></li> - * <li><tt>Accept</tt>: <tt>application/json, text/plain, */*</tt></li> - * <li><tt>X-Requested-With</tt>: <tt>XMLHttpRequest</tt></li> - * </ul> - * - * @param {number=} timeout Timeout in ms, when the request will be aborted - * @returns {XMLHttpRequest|undefined} Raw XMLHttpRequest object or undefined when JSONP method - * - * @description - * Send ajax request - * - * TODO(vojta): change signature of this method to (method, url, data, headers, callback) - */ - self.xhr = function(method, url, post, callback, headers, timeout) { - outstandingRequestCount ++; - if (lowercase(method) == 'jsonp') { - var callbackId = ("angular_" + Math.random() + '_' + (idCounter++)).replace(/\d\./, ''); - window[callbackId] = function(data) { - window[callbackId].data = data; - }; - - var script = self.addJs(url.replace('JSON_CALLBACK', callbackId), function() { - if (window[callbackId].data) { - completeOutstandingRequest(callback, 200, window[callbackId].data); - } else { - completeOutstandingRequest(callback, -2); - } - delete window[callbackId]; - body[0].removeChild(script); - }); - } else { - var xhr = new XHR(); - xhr.open(method, url, true); - forEach(headers, function(value, key) { - if (value) xhr.setRequestHeader(key, value); - }); - - var status; - xhr.send(post || ''); - - // IE6, IE7 bug - does sync when serving from cache - if (xhr.readyState == 4) { - setTimeout(function() { - completeOutstandingRequest(callback, fixStatus(status || xhr.status), xhr.responseText); - }, 0); - } else { - xhr.onreadystatechange = function() { - if (xhr.readyState == 4) { - completeOutstandingRequest(callback, fixStatus(status || xhr.status), - xhr.responseText); - } - }; - } - - if (timeout > 0) { - setTimeout(function() { - status = -1; - xhr.abort(); - }, timeout); - } - - return xhr; - } - }; - /** * @private * Note: this method is used only by scenario runner @@ -502,6 +408,6 @@ function Browser(window, document, body, XHR, $log, $sniffer) { function $BrowserProvider(){ this.$get = ['$window', '$log', '$sniffer', '$document', function( $window, $log, $sniffer, $document){ - return new Browser($window, $document, $document.find('body'), XHR, $log, $sniffer); + return new Browser($window, $document, $document.find('body'), $log, $sniffer); }]; } diff --git a/src/service/httpBackend.js b/src/service/httpBackend.js index af3de970..28700940 100644 --- a/src/service/httpBackend.js +++ b/src/service/httpBackend.js @@ -1,6 +1,86 @@ +var XHR = window.XMLHttpRequest || function() { + try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} + try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {} + try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {} + throw new Error("This browser does not support XMLHttpRequest."); +}; + + +/** + * @ngdoc object + * @name angular.module.ng.$httpBackend + * @requires $browser + * @requires $window + * @requires $document + * + * @description + */ function $HttpBackendProvider() { - this.$get = ['$browser', function($browser) { - return $browser.xhr; + this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { + return createHttpBackend($browser, XHR, $browser.defer, $window, $document[0].body); }]; } +function createHttpBackend($browser, XHR, $browserDefer, $window, body) { + var idCounter = 0; + + function completeRequest(callback, status, response) { + // normalize IE bug (http://bugs.jquery.com/ticket/1450) + callback(status == 1223 ? 204 : status, response); + $browser.$$completeOutstandingRequest(noop); + } + + // TODO(vojta): fix the signature + return function(method, url, post, callback, headers, timeout) { + $browser.$$incOutstandingRequestCount(); + + if (lowercase(method) == 'jsonp') { + var callbackId = ('angular_' + Math.random() + '_' + (idCounter++)).replace(/\d\./, ''); + $window[callbackId] = function(data) { + $window[callbackId].data = data; + }; + + var script = $browser.addJs(url.replace('JSON_CALLBACK', callbackId), null, function() { + if ($window[callbackId].data) { + completeRequest(callback, 200, $window[callbackId].data); + } else { + completeRequest(callback, -2); + } + delete $window[callbackId]; + body.removeChild(script); + }); + } else { + var xhr = new XHR(); + xhr.open(method, url, true); + forEach(headers, function(value, key) { + if (value) xhr.setRequestHeader(key, value); + }); + + var status; + xhr.send(post || ''); + + // IE6, IE7 bug - does sync when serving from cache + if (xhr.readyState == 4) { + $browserDefer(function() { + completeRequest(callback, status || xhr.status, xhr.responseText); + }, 0); + } else { + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + completeRequest(callback, status || xhr.status, xhr.responseText); + } + }; + } + + if (timeout > 0) { + $browserDefer(function() { + status = -1; + xhr.abort(); + }, timeout); + } + + return xhr; + } + }; +} + |
