diff options
| author | Vojta Jina | 2011-08-23 22:19:36 +0200 | 
|---|---|---|
| committer | Igor Minar | 2011-11-30 11:17:22 -0500 | 
| commit | 5ad0c7d0e4a2aff071d3afb181fa618c982ce991 (patch) | |
| tree | f54cd54714acab092f4268085c3f46d7b6eafee7 /src | |
| parent | 540701a8d8d843c61192f1105b210d870099cea8 (diff) | |
| download | angular.js-5ad0c7d0e4a2aff071d3afb181fa618c982ce991.tar.bz2 | |
feat($httpBackend): extract $browser.xhr into separate service
- remove whole $browser.xhr stuff
- remove whole mock $browser.xhr stuff
- add $httpBackend service + migrate unit tests from $browser
- add temporary API to access $browser's outstandingRequests count
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; +    } +  }; +} + | 
