diff options
| -rw-r--r-- | angularFiles.js | 1 | ||||
| -rw-r--r-- | src/AngularPublic.js | 1 | ||||
| -rw-r--r-- | src/angular-mocks.js | 167 | ||||
| -rw-r--r-- | src/service/http.js | 13 | ||||
| -rw-r--r-- | src/service/httpBackend.js | 6 | ||||
| -rw-r--r-- | test/angular-mocksSpec.js | 397 | ||||
| -rw-r--r-- | test/service/httpSpec.js | 735 | ||||
| -rw-r--r-- | test/widgetsSpec.js | 68 | 
8 files changed, 944 insertions, 444 deletions
diff --git a/angularFiles.js b/angularFiles.js index 6871c2a4..a67aa6cc 100644 --- a/angularFiles.js +++ b/angularFiles.js @@ -33,6 +33,7 @@ angularFiles = {      'src/service/sniffer.js',      'src/service/window.js',      'src/service/http.js', +    'src/service/httpBackend.js',      'src/service/locale.js',      'src/directives.js',      'src/markups.js', diff --git a/src/AngularPublic.js b/src/AngularPublic.js index df309189..4d94e901 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -78,6 +78,7 @@ function ngModule($provide, $injector) {    $provide.service('$filter', $FilterProvider);    $provide.service('$formFactory', $FormFactoryProvider);    $provide.service('$http', $HttpProvider); +  $provide.service('$httpBackend', $HttpBackendProvider);    $provide.service('$location', $LocationProvider);    $provide.service('$log', $LogProvider);    $provide.service('$parse', $ParseProvider); diff --git a/src/angular-mocks.js b/src/angular-mocks.js index 73bc4cbd..bc0578f5 100644 --- a/src/angular-mocks.js +++ b/src/angular-mocks.js @@ -21,6 +21,7 @@ angular.module.ngMock = function($provide){    $provide.service('$browser', angular.module.ngMock.$BrowserProvider);    $provide.service('$exceptionHandler', angular.module.ngMock.$ExceptionHandlerProvider);    $provide.service('$log', angular.module.ngMock.$LogProvider); +  $provide.service('$httpBackend', angular.module.ngMock.$HttpBackendProvider);  };  angular.module.ngMock.$inject = ['$provide']; @@ -38,8 +39,6 @@ angular.module.ngMock.$inject = ['$provide'];   *   * The following apis can be used in tests:   * - * - {@link #xhr} — enables testing of code that uses - *   the {@link angular.module.ng.$xhr $xhr service} to make XmlHttpRequests.   * - $browser.defer — enables testing of code that uses   *   {@link angular.module.ng.$defer $defer} for executing functions via the `setTimeout` api.   */ @@ -720,6 +719,170 @@ angular.module.ngMock.dump = function(object){    }  }; +/** + * @ngdoc object + * @name angular.module.ngMock.$httpBackend + */ +angular.module.ngMock.$HttpBackendProvider = function() { +  this.$get = function() { +    var definitions = [], +        expectations = [], +        responses = []; + +    function createResponse(status, data, headers) { +      return angular.isNumber(status) ? [status, data, headers] : [200, status, data]; +    } + +    // TODO(vojta): change params to: method, url, data, headers, callback +    function $httpBackend(method, url, data, callback, headers) { +      var xhr = new MockXhr(), +          expectation = expectations[0], +          wasExpected = false; + +      if (expectation && expectation.match(method, url)) { +        if (!expectation.matchData(data)) +          throw Error('Expected ' + method + ' ' + url + ' with different data'); + +        if (!expectation.matchHeaders(headers)) +          throw Error('Expected ' + method + ' ' + url + ' with different headers'); + +        expectations.shift(); + +        if (expectation.response) { +          responses.push(function() { +            xhr.$$headers = expectation.response[2]; +            callback(expectation.response[0], expectation.response[1]); +          }); +          return method == 'JSONP' ? undefined : xhr; +        } +        wasExpected = true; +      } + +      var i = -1, definition; +      while ((definition = definitions[++i])) { +        if (definition.match(method, url, data, headers || {})) { +          if (!definition.response) throw Error('No response defined !'); +          responses.push(function() { +            var response = angular.isFunction(definition.response) ? +                           definition.response(method, url, data, headers) : definition.response; +            xhr.$$headers = response[2]; +            callback(response[0], response[1]); +          }); +          return method == 'JSONP' ? undefined : xhr; +        } +      } +      throw wasExpected ? Error('No response defined !') : +                          Error('Unexpected request: ' + method + ' ' + url); +    } + +    $httpBackend.when = function(method, url, data, headers) { +      var definition = new MockHttpExpectation(method, url, data, headers); +      definitions.push(definition); +      return { +        then: function(status, data, headers) { +          definition.response = angular.isFunction(status) ? status : createResponse(status, data, headers); +        } +      }; +    }; + +    $httpBackend.expect = function(method, url, data, headers) { +      var expectation = new MockHttpExpectation(method, url, data, headers); +      expectations.push(expectation); +      return { +        respond: function(status, data, headers) { +          expectation.response = createResponse(status, data, headers); +        } +      }; +    }; + +    $httpBackend.flush = function(count) { +      count = count || responses.length; +      while (count--) { +        if (!responses.length) throw Error('No more pending requests'); +        responses.shift()(); +      } +    }; + + + +    $httpBackend.verifyExpectations = function() { +      if (expectations.length) { +        throw Error('Unsatisfied requests: ' + expectations.join(', ')); +      } +    }; + +    $httpBackend.resetExpectations = function() { +      expectations = []; +      responses = []; +    }; + +    return $httpBackend; +  }; +}; + +function MockHttpExpectation(method, url, data, headers) { + +  this.match = function(m, u, d, h) { +    if (method != m) return false; +    if (!this.matchUrl(u)) return false; +    if (angular.isDefined(d) && !this.matchData(d)) return false; +    if (angular.isDefined(h) && !this.matchHeaders(h)) return false; +    return true; +  }; + +  this.matchUrl = function(u) { +    if (!url) return true; +    if (angular.isFunction(url.test)) { +      if (!url.test(u)) return false; +    } else if (url != u) return false; + +    return true; +  }; + +  this.matchHeaders = function(h) { +    if (angular.isUndefined(headers)) return true; +    if (angular.isFunction(headers)) { +      if (!headers(h)) return false; +    } else if (!angular.equals(headers, h)) return false; + +    return true; +  }; + +  this.matchData = function(d) { +    if (angular.isUndefined(data)) return true; +    if (data && angular.isFunction(data.test)) { +      if (!data.test(d)) return false; +    } else if (data != d) return false; + +    return true; +  }; + +  this.toString = function() { +    return method + ' ' + url; +  }; +} + +function MockXhr() { + +  // hack for testing $http +  MockXhr.$$lastInstance = this; + +  this.getResponseHeader = function(name) { +    return this.$$headers[name]; +  }; + +  this.getAllResponseHeaders = function() { +    var lines = []; + +    angular.forEach(this.$$headers, function(value, key) { +      lines.push(key + ': ' + value); +    }); +    return lines.join('\n'); +  }; + +  this.abort = noop; +} +  window.jstestdriver && (function(window){    /**     * Global method to output any number of objects into JSTD console. Useful for debugging. diff --git a/src/service/http.js b/src/service/http.js index 13621f90..087c3809 100644 --- a/src/service/http.js +++ b/src/service/http.js @@ -51,6 +51,7 @@ function transform(data, fns, param) {  /**   * @ngdoc object   * @name angular.module.ng.$http + * @requires $httpBacked   * @requires $browser   * @requires $exceptionHandler   * @requires $cacheFactory @@ -85,8 +86,8 @@ function $HttpProvider() {      }    }; -  this.$get = ['$browser', '$exceptionHandler', '$cacheFactory', '$rootScope', -      function($browser, $exceptionHandler, $cacheFactory, $rootScope) { +  this.$get = ['$httpBackend', '$browser', '$exceptionHandler', '$cacheFactory', '$rootScope', +      function($httpBackend, $browser, $exceptionHandler, $cacheFactory, $rootScope) {    var cache = $cacheFactory('$http'),        pendingRequestsCount = 0; @@ -235,7 +236,7 @@ function $HttpProvider() {    /**     * Represents Request object, returned by $http()     * -   * !!! ACCESS CLOSURE VARS: $browser, $config, $log, $rootScope, cache, pendingRequestsCount +   * !!! ACCESS CLOSURE VARS: $httpBackend, $browser, $config, $log, $rootScope, cache, pendingRequestsCount     */    function XhrFuture() {      var rawRequest, cfg = {}, callbacks = [], @@ -243,7 +244,7 @@ function $HttpProvider() {          parsedHeaders;      /** -     * Callback registered to $browser.xhr: +     * Callback registered to $httpBackend():       *  - caches the response if desired       *  - calls fireCallbacks()       *  - clears the reference to raw request object @@ -265,7 +266,7 @@ function $HttpProvider() {       * Fire all registered callbacks for given status code       *       * This method when: -     *  - serving response from real request ($browser.xhr callback) +     *  - serving response from real request       *  - serving response from cache       *       * It does: @@ -368,7 +369,7 @@ function $HttpProvider() {            fireCallbacks(fromCache[1], fromCache[0]);          });        } else { -        rawRequest = $browser.xhr(cfg.method, cfg.url, data, done, headers, cfg.timeout); +        rawRequest = $httpBackend(cfg.method, cfg.url, data, done, headers, cfg.timeout);        }        pendingRequestsCount++; diff --git a/src/service/httpBackend.js b/src/service/httpBackend.js new file mode 100644 index 00000000..af3de970 --- /dev/null +++ b/src/service/httpBackend.js @@ -0,0 +1,6 @@ +function $HttpBackendProvider() { +  this.$get = ['$browser', function($browser) { +    return $browser.xhr; +  }]; +} + diff --git a/test/angular-mocksSpec.js b/test/angular-mocksSpec.js index acb019c7..4551d11d 100644 --- a/test/angular-mocksSpec.js +++ b/test/angular-mocksSpec.js @@ -342,4 +342,401 @@ describe('mocks', function() {        expect(count).toBe(2);      });    }); + + +  describe('$httpBackend', function() { +    var hb, callback; + +    beforeEach(inject(function($httpBackend) { +      callback = jasmine.createSpy('callback'); +      hb = $httpBackend; +    })); + + +    it('should respond with first matched definition', function() { +      hb.when('GET', '/url1').then(200, 'content', {}); +      hb.when('GET', '/url1').then(201, 'another', {}); + +      callback.andCallFake(function(status, response) { +        expect(status).toBe(200); +        expect(response).toBe('content'); +      }); + +      hb('GET', '/url1', null, callback); +      expect(callback).not.toHaveBeenCalled(); +      hb.flush(); +      expect(callback).toHaveBeenCalledOnce(); +    }); + + +    it('should throw error when unexpected request', function() { +      hb.when('GET', '/url1').then(200, 'content'); +      expect(function() { +        hb('GET', '/xxx'); +      }).toThrow('Unexpected request: GET /xxx'); +    }); + + +    it('should match headers if specified', function() { +      hb.when('GET', '/url', null, {'X': 'val1'}).then(201, 'content1'); +      hb.when('GET', '/url', null, {'X': 'val2'}).then(202, 'content2'); +      hb.when('GET', '/url').then(203, 'content3'); + +      hb('GET', '/url', null, function(status, response) { +        expect(status).toBe(203); +        expect(response).toBe('content3'); +      }); + +      hb('GET', '/url', null, function(status, response) { +        expect(status).toBe(201); +        expect(response).toBe('content1'); +      }, {'X': 'val1'}); + +      hb('GET', '/url', null, function(status, response) { +        expect(status).toBe(202); +        expect(response).toBe('content2'); +      }, {'X': 'val2'}); + +      hb.flush(); +    }); + + +    it('should match data if specified', function() { +      hb.when('GET', '/a/b', '{a: true}').then(201, 'content1'); +      hb.when('GET', '/a/b').then(202, 'content2'); + +      hb('GET', '/a/b', '{a: true}', function(status, response) { +        expect(status).toBe(201); +        expect(response).toBe('content1'); +      }); + +      hb('GET', '/a/b', null, function(status, response) { +        expect(status).toBe(202); +        expect(response).toBe('content2'); +      }); + +      hb.flush(); +    }); + + +    it('should match only method', function() { +      hb.when('GET').then(202, 'c'); +      callback.andCallFake(function(status, response) { +        expect(status).toBe(202); +        expect(response).toBe('c'); +      }); + +      hb('GET', '/some', null, callback, {}); +      hb('GET', '/another', null, callback, {'X-Fake': 'Header'}); +      hb('GET', '/third', 'some-data', callback, {}); +      hb.flush(); + +      expect(callback).toHaveBeenCalled(); +    }); + + +    it('should expose given headers', function() { +      hb.when('GET', '/u1').then(200, null, {'X-Fake': 'Header', 'Content-Type': 'application/json'}); +      var xhr = hb('GET', '/u1', null, noop, {}); +      hb.flush(); +      expect(xhr.getResponseHeader('X-Fake')).toBe('Header'); +      expect(xhr.getAllResponseHeaders()).toBe('X-Fake: Header\nContent-Type: application/json'); +    }); + + +    it('should preserve the order of requests', function() { +      hb.when('GET', '/url1').then(200, 'first'); +      hb.when('GET', '/url2').then(201, 'second'); + +      hb('GET', '/url2', null, callback); +      hb('GET', '/url1', null, callback); + +      hb.flush(); + +      expect(callback.callCount).toBe(2); +      expect(callback.argsForCall[0]).toEqual([201, 'second']); +      expect(callback.argsForCall[1]).toEqual([200, 'first']); +    }); + + +    it('then() should take function', function() { +      hb.when('GET', '/some').then(function(m, u, d, h) { +        return [301, m + u + ';' + d + ';a=' + h.a, {'Connection': 'keep-alive'}]; +      }); + +      var xhr = hb('GET', '/some', 'data', callback, {a: 'b'}); +      hb.flush(); + +      expect(callback).toHaveBeenCalledOnce(); +      expect(callback.mostRecentCall.args[0]).toBe(301); +      expect(callback.mostRecentCall.args[1]).toBe('GET/some;data;a=b'); +      expect(xhr.getResponseHeader('Connection')).toBe('keep-alive'); +    }); + + +    it('expect() should require specified order', function() { +      hb.expect('GET', '/url1').respond(200, ''); +      hb.expect('GET', '/url2').respond(200, ''); + +      expect(function() { +        hb('GET', '/url2', null, noop, {}); +      }).toThrow('Unexpected request: GET /url2'); +    }); + + +    it('expect() should have precendence over when()', function() { +      callback.andCallFake(function(status, response) { +        expect(status).toBe(300); +        expect(response).toBe('expect'); +      }); + +      hb.when('GET', '/url').then(200, 'when'); +      hb.expect('GET', '/url').respond(300, 'expect'); + +      hb('GET', '/url', null, callback, {}); +      hb.flush(); +      expect(callback).toHaveBeenCalledOnce(); +    }); + + +    it ('should throw exception when only headers differes from expectation', function() { +      hb.when('GET').then(200, '', {}); +      hb.expect('GET', '/match', undefined, {'Content-Type': 'application/json'}); + +      expect(function() { +        hb('GET', '/match', null, noop, {}); +      }).toThrow('Expected GET /match with different headers'); +    }); + + +    it ('should throw exception when only data differes from expectation', function() { +      hb.when('GET').then(200, '', {}); +      hb.expect('GET', '/match', 'some-data'); + +      expect(function() { +        hb('GET', '/match', 'different', noop, {}); +      }).toThrow('Expected GET /match with different data'); +    }); + + +    it('expect() should without respond() and use then()', function() { +      callback.andCallFake(function(status, response) { +        expect(status).toBe(201); +        expect(response).toBe('data'); +      }); + +      hb.when('GET', '/some').then(201, 'data'); +      hb.expect('GET', '/some'); +      hb('GET', '/some', null, callback); +      hb.flush(); + +      expect(callback).toHaveBeenCalled(); +      expect(function() { hb.verifyExpectations(); }).not.toThrow(); +    }); + + +    it('flush() should not flush requests fired during callbacks', function() { +      // regression +      hb.when('GET').then(200, ''); +      hb('GET', '/some', null, function() { +        hb('GET', '/other', null, callback); +      }); + +      hb.flush(); +      expect(callback).not.toHaveBeenCalled(); +    }); + + +    it('flush() should flush given number of pending requests', function() { +      hb.when('GET').then(200, ''); +      hb('GET', '/some', null, callback); +      hb('GET', '/some', null, callback); +      hb('GET', '/some', null, callback); + +      hb.flush(2); +      expect(callback).toHaveBeenCalled(); +      expect(callback.callCount).toBe(2); +    }); + + +    it('flush() should throw exception when flushing more requests than pending', function() { +      hb.when('GET').then(200, ''); +      hb('GET', '/url', null, callback); + +      expect(function() {hb.flush(2);}).toThrow('No more pending requests'); +      expect(callback).toHaveBeenCalledOnce(); +    }); + + +    it('respond() should set default status 200 if not defined', function() { +      callback.andCallFake(function(status, response) { +        expect(status).toBe(200); +        expect(response).toBe('some-data'); +      }); + +      hb.expect('GET', '/url1').respond('some-data'); +      hb.expect('GET', '/url2').respond('some-data', {'X-Header': 'true'}); +      hb('GET', '/url1', null, callback); +      hb('GET', '/url2', null, callback); +      hb.flush(); +      expect(callback).toHaveBeenCalled(); +      expect(callback.callCount).toBe(2); +    }); + + +    it('then() should set default status 200 if not defined', function() { +      callback.andCallFake(function(status, response) { +        expect(status).toBe(200); +        expect(response).toBe('some-data'); +      }); + +      hb.when('GET', '/url1').then('some-data'); +      hb.when('GET', '/url2').then('some-data', {'X-Header': 'true'}); +      hb('GET', '/url1', null, callback); +      hb('GET', '/url2', null, callback); +      hb.flush(); +      expect(callback).toHaveBeenCalled(); +      expect(callback.callCount).toBe(2); +    }); + + +    it('should respond with definition if no response for expectation', function() { +      callback.andCallFake(function(status, response) { +        expect(status).toBe(201); +        expect(response).toBe('def-response'); +      }); + +      hb.when('GET').then(201, 'def-response'); +      hb.expect('GET', '/some-url'); + +      hb('GET', '/some-url', null, callback); +      hb.flush(); +      expect(callback).toHaveBeenCalledOnce(); +      hb.verifyExpectations(); +    }); + + +    it('should throw an exception if no response defined', function() { +      hb.when('GET', '/test'); +      expect(function() { +        hb('GET', '/test', null, callback); +      }).toThrow('No response defined !'); +    }); + + +    it('should throw an exception if no response for expection and no definition', function() { +      hb.expect('GET', '/url'); +      expect(function() { +        hb('GET', '/url', null, callback); +      }).toThrow('No response defined !'); +    }); + + +    it('should respond undefined when JSONP method', function() { +      hb.when('JSONP', '/url1').then(200); +      hb.expect('JSONP', '/url2').respond(200); + +      expect(hb('JSONP', '/url1')).toBeUndefined(); +      expect(hb('JSONP', '/url2')).toBeUndefined(); +    }); + + +    describe('verify', function() { + +      it('should throw exception if not all expectations were satisfied', function() { +        hb.expect('POST', '/u1', 'ddd').respond(201, '', {}); +        hb.expect('GET', '/u2').respond(200, '', {}); +        hb.expect('POST', '/u3').respond(201, '', {}); + +        hb('POST', '/u1', 'ddd', noop, {}); + +        expect(function() {hb.verifyExpectations();}) +          .toThrow('Unsatisfied requests: GET /u2, POST /u3'); +      }); + + +      it('should do nothing when no expectation', function() { +        hb.when('DELETE', '/some').then(200, ''); + +        expect(function() {hb.verifyExpectations();}).not.toThrow(); +      }); + + +      it('should do nothing when all expectations satisfied', function() { +        hb.expect('GET', '/u2').respond(200, '', {}); +        hb.expect('POST', '/u3').respond(201, '', {}); +        hb.when('DELETE', '/some').then(200, ''); + +        hb('GET', '/u2', noop); +        hb('POST', '/u3', noop); + +        expect(function() {hb.verifyExpectations();}).not.toThrow(); +      }); +    }); + + +    describe('reset', function() { + +      it('should remove all expectations', function() { +        hb.expect('GET', '/u2').respond(200, '', {}); +        hb.expect('POST', '/u3').respond(201, '', {}); +        hb.resetExpectations(); + +        expect(function() {hb.verifyExpectations();}).not.toThrow(); +      }); + + +      it('should remove all responses', function() { +        hb.expect('GET', '/url').respond(200, '', {}); +        hb('GET', '/url', null, callback, {}); +        hb.resetExpectations(); +        hb.flush(); + +        expect(callback).not.toHaveBeenCalled(); +      }); +    }); + + +    describe('MockHttpExpectation', function() { + +      it('should accept url as regexp', function() { +        var exp = new MockHttpExpectation('GET', /^\/x/); + +        expect(exp.match('GET', '/x')).toBe(true); +        expect(exp.match('GET', '/xxx/x')).toBe(true); +        expect(exp.match('GET', 'x')).toBe(false); +        expect(exp.match('GET', 'a/x')).toBe(false); +      }); + + +      it('should accept data as regexp', function() { +        var exp = new MockHttpExpectation('POST', '/url', /\{.*?\}/); + +        expect(exp.match('POST', '/url', '{"a": "aa"}')).toBe(true); +        expect(exp.match('POST', '/url', '{"one": "two"}')).toBe(true); +        expect(exp.match('POST', '/url', '{"one"')).toBe(false); +      }); + + +      it('should ignore data only if undefined (not null or false)', function() { +        var exp = new MockHttpExpectation('POST', '/url', null); +        expect(exp.matchData(null)).toBe(true); +        expect(exp.matchData('some-data')).toBe(false); + +        exp = new MockHttpExpectation('POST', '/url', undefined); +        expect(exp.matchData(null)).toBe(true); +        expect(exp.matchData('some-data')).toBe(true); +      }); + + +      it('should accept headers as function', function() { +        var exp = new MockHttpExpectation('GET', '/url', undefined, function(h) { +          return h['Content-Type'] == 'application/json'; +        }); + +        expect(exp.matchHeaders({})).toBe(false); +        expect(exp.matchHeaders({'Content-Type': 'application/json', 'X-Another': 'true'})).toBe(true); +      }); +    }); +  });  }); diff --git a/test/service/httpSpec.js b/test/service/httpSpec.js index 196a57ed..75e85359 100644 --- a/test/service/httpSpec.js +++ b/test/service/httpSpec.js @@ -3,98 +3,68 @@  // TODO(vojta): refactor these tests to use new inject() syntax  describe('$http', function() { -  var $http, $browser, $exceptionHandler, // services -      method, url, data, headers, timeout, // passed arguments -      onSuccess, onError, // callback spies -      scope, errorLogs, respond, rawXhrObject, future; +  var $http, $browser, $exceptionHandler, $httpBackend, +      scope, callback, future, callback;    beforeEach(inject(function($injector) {      $injector.get('$exceptionHandlerProvider').mode('log');      scope = $injector.get('$rootScope');      $http = $injector.get('$http');      $browser = $injector.get('$browser'); +    $httpBackend = $injector.get('$httpBackend');      $exceptionHandler = $injector.get('$exceptionHandler'); - -    // TODO(vojta): move this into mock browser ? -    respond = method = url = data = headers = null; -    rawXhrObject = { -      abort: jasmine.createSpy('request.abort'), -      getResponseHeader: function(h) {return h + '-val';}, -      getAllResponseHeaders: function() { -        return 'content-encoding: gzip\nserver: Apache\n'; -      } -    }; -      spyOn(scope, '$apply'); -    spyOn($browser, 'xhr').andCallFake(function(m, u, d, c, h, t) { -      method = m; -      url = u; -      data = d; -      respond = c; -      headers = h; -      timeout = t; -      return rawXhrObject; -    }); +    callback = jasmine.createSpy('callback');    }));    afterEach(function() { -    // expect($exceptionHandler.errors.length).toBe(0); +    if ($exceptionHandler.errors.length) throw $exceptionHandler.errors; +    $httpBackend.verifyExpectations();    }); -  function doCommonXhr(method, url) { -    future = $http({method: method || 'GET', url: url || '/url'}); - -    onSuccess = jasmine.createSpy('on200'); -    onError = jasmine.createSpy('on400'); -    future.on('200', onSuccess); -    future.on('400', onError); - -    return future; -  } -    it('should do basic request', function() { +    $httpBackend.expect('GET', '/url').respond('');      $http({url: '/url', method: 'GET'}); -    expect($browser.xhr).toHaveBeenCalledOnce(); -    expect(url).toBe('/url'); -    expect(method).toBe('GET');    });    it('should pass data if specified', function() { +    $httpBackend.expect('POST', '/url', 'some-data').respond('');      $http({url: '/url', method: 'POST', data: 'some-data'}); -    expect($browser.xhr).toHaveBeenCalledOnce(); -    expect(data).toBe('some-data');    }); -  it('should pass timeout if specified', function() { -    $http({url: '/url', method: 'POST', timeout: 5000}); -    expect($browser.xhr).toHaveBeenCalledOnce(); -    expect(timeout).toBe(5000); -  }); +  // TODO(vojta): test passing timeout    describe('callbacks', function() { -    beforeEach(doCommonXhr); +    function throwing(name) { +      return function() { +        throw name; +      }; +    }      it('should log exceptions', function() { -      onSuccess.andThrow('exception in success callback'); -      onError.andThrow('exception in error callback'); +      $httpBackend.expect('GET', '/url1').respond(200, 'content'); +      $httpBackend.expect('GET', '/url2').respond(400, ''); -      respond(200, 'content'); -      expect($exceptionHandler.errors.pop()).toContain('exception in success callback'); +      $http({url: '/url1', method: 'GET'}).on('200', throwing('exception in success callback')); +      $http({url: '/url2', method: 'GET'}).on('400', throwing('exception in error callback')); +      $httpBackend.flush(); -      respond(400, ''); -      expect($exceptionHandler.errors.pop()).toContain('exception in error callback'); +      expect($exceptionHandler.errors.shift()).toContain('exception in success callback'); +      expect($exceptionHandler.errors.shift()).toContain('exception in error callback');      });      it('should log more exceptions', function() { -      onError.andThrow('exception in error callback'); -      future.on('500', onError).on('50x', onError); -      respond(500, ''); +      $httpBackend.expect('GET', '/url').respond(500, ''); +      $http({url: '/url', method: 'GET'}) +        .on('500', throwing('exception in error callback')) +        .on('5xx', throwing('exception in error callback')); +      $httpBackend.flush();        expect($exceptionHandler.errors.length).toBe(2);        $exceptionHandler.errors = []; @@ -102,82 +72,76 @@ describe('$http', function() {      it('should get response as first param', function() { -      respond(200, 'response'); -      expect(onSuccess).toHaveBeenCalledOnce(); -      expect(onSuccess.mostRecentCall.args[0]).toBe('response'); +      $httpBackend.expect('GET', '/url').respond('some-content'); +      $http({url: '/url', method: 'GET'}).on('200', callback); +      $httpBackend.flush(); -      respond(400, 'empty'); -      expect(onError).toHaveBeenCalledOnce(); -      expect(onError.mostRecentCall.args[0]).toBe('empty'); +      expect(callback).toHaveBeenCalledOnce(); +      expect(callback.mostRecentCall.args[0]).toBe('some-content');      });      it('should get status code as second param', function() { -      respond(200, 'response'); -      expect(onSuccess).toHaveBeenCalledOnce(); -      expect(onSuccess.mostRecentCall.args[1]).toBe(200); +      $httpBackend.expect('GET', '/url').respond(250, 'some-content'); +      $http({url: '/url', method: 'GET'}).on('2xx', callback); +      $httpBackend.flush(); -      respond(400, 'empty'); -      expect(onError).toHaveBeenCalledOnce(); -      expect(onError.mostRecentCall.args[1]).toBe(400); +      expect(callback).toHaveBeenCalledOnce(); +      expect(callback.mostRecentCall.args[1]).toBe(250);      });    });    describe('response headers', function() { -    var callback; - -    beforeEach(function() { -      callback = jasmine.createSpy('callback'); -    }); -      it('should return single header', function() { +      $httpBackend.expect('GET', '/url').respond('', {'date': 'date-val'});        callback.andCallFake(function(r, s, header) {          expect(header('date')).toBe('date-val');        });        $http({url: '/url', method: 'GET'}).on('200', callback); -      respond(200, ''); +      $httpBackend.flush();        expect(callback).toHaveBeenCalledOnce();      });      it('should return null when single header does not exist', function() { +      $httpBackend.expect('GET', '/url').respond('', {'Some-Header': 'Fake'});        callback.andCallFake(function(r, s, header) {          header(); // we need that to get headers parsed first          expect(header('nothing')).toBe(null);        });        $http({url: '/url', method: 'GET'}).on('200', callback); -      respond(200, ''); +      $httpBackend.flush();        expect(callback).toHaveBeenCalledOnce();      });      it('should return all headers as object', function() { +      $httpBackend.expect('GET', '/url').respond('', {'content-encoding': 'gzip', 'server': 'Apache'});        callback.andCallFake(function(r, s, header) {          expect(header()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'});        });        $http({url: '/url', method: 'GET'}).on('200', callback); -      respond(200, ''); +      $httpBackend.flush();        expect(callback).toHaveBeenCalledOnce();      });      it('should return empty object for jsonp request', function() { -      // jsonp doesn't return raw object -      rawXhrObject = undefined;        callback.andCallFake(function(r, s, headers) {          expect(headers()).toEqual({});        }); +      $httpBackend.expect('JSONP', '/some').respond(200);        $http({url: '/some', method: 'JSONP'}).on('200', callback); -      respond(200, ''); +      $httpBackend.flush();        expect(callback).toHaveBeenCalledOnce();      });    }); @@ -250,202 +214,192 @@ describe('$http', function() {    describe('request headers', function() {      it('should send custom headers', function() { +      $httpBackend.expect('GET', '/url', undefined, function(headers) { +        return headers['Custom'] == 'header' && headers['Content-Type'] == 'application/json'; +      }).respond(''); +        $http({url: '/url', method: 'GET', headers: {          'Custom': 'header',          'Content-Type': 'application/json'        }}); -      expect(headers['Custom']).toEqual('header'); -      expect(headers['Content-Type']).toEqual('application/json'); +      $httpBackend.flush();      });      it('should set default headers for GET request', function() { -      $http({url: '/url', method: 'GET', headers: {}}); +      $httpBackend.expect('GET', '/url', undefined, function(headers) { +        return headers['Accept'] == 'application/json, text/plain, */*' && +               headers['X-Requested-With'] == 'XMLHttpRequest'; +      }).respond(''); -      expect(headers['Accept']).toBe('application/json, text/plain, */*'); -      expect(headers['X-Requested-With']).toBe('XMLHttpRequest'); +      $http({url: '/url', method: 'GET', headers: {}}); +      $httpBackend.flush();      });      it('should set default headers for POST request', function() { -      $http({url: '/url', method: 'POST', headers: {}}); +      $httpBackend.expect('POST', '/url', undefined, function(headers) { +        return headers['Accept'] == 'application/json, text/plain, */*' && +               headers['X-Requested-With'] == 'XMLHttpRequest' && +               headers['Content-Type'] == 'application/json'; +      }).respond(''); -      expect(headers['Accept']).toBe('application/json, text/plain, */*'); -      expect(headers['X-Requested-With']).toBe('XMLHttpRequest'); -      expect(headers['Content-Type']).toBe('application/json'); +      $http({url: '/url', method: 'POST', headers: {}}); +      $httpBackend.flush();      });      it('should set default headers for PUT request', function() { -      $http({url: '/url', method: 'PUT', headers: {}}); +      $httpBackend.expect('PUT', '/url', undefined, function(headers) { +        return headers['Accept'] == 'application/json, text/plain, */*' && +               headers['X-Requested-With'] == 'XMLHttpRequest' && +               headers['Content-Type'] == 'application/json'; +      }).respond(''); -      expect(headers['Accept']).toBe('application/json, text/plain, */*'); -      expect(headers['X-Requested-With']).toBe('XMLHttpRequest'); -      expect(headers['Content-Type']).toBe('application/json'); +      $http({url: '/url', method: 'PUT', headers: {}}); +      $httpBackend.flush();      });      it('should set default headers for custom HTTP method', function() { -      $http({url: '/url', method: 'FOO', headers: {}}); +      $httpBackend.expect('FOO', '/url', undefined, function(headers) { +        return headers['Accept'] == 'application/json, text/plain, */*' && +               headers['X-Requested-With'] == 'XMLHttpRequest'; +      }).respond(''); -      expect(headers['Accept']).toBe('application/json, text/plain, */*'); -      expect(headers['X-Requested-With']).toBe('XMLHttpRequest'); +      $http({url: '/url', method: 'FOO', headers: {}}); +      $httpBackend.flush();      });      it('should override default headers with custom', function() { +      $httpBackend.expect('POST', '/url', undefined, function(headers) { +        return headers['Accept'] == 'Rewritten' && +               headers['X-Requested-With'] == 'XMLHttpRequest' && +               headers['Content-Type'] == 'Rewritten'; +      }).respond(''); +        $http({url: '/url', method: 'POST', headers: {          'Accept': 'Rewritten',          'Content-Type': 'Rewritten'        }}); - -      expect(headers['Accept']).toBe('Rewritten'); -      expect(headers['X-Requested-With']).toBe('XMLHttpRequest'); -      expect(headers['Content-Type']).toBe('Rewritten'); +      $httpBackend.flush();      });      it('should set the XSRF cookie into a XSRF header', function() { +      function checkXSRF(secret) { +        return function(headers) { +          return headers['X-XSRF-TOKEN'] == secret; +        }; +      } +        $browser.cookies('XSRF-TOKEN', 'secret'); +      $httpBackend.expect('GET', '/url', undefined, checkXSRF('secret')).respond(''); +      $httpBackend.expect('POST', '/url', undefined, checkXSRF('secret')).respond(''); +      $httpBackend.expect('PUT', '/url', undefined, checkXSRF('secret')).respond(''); +      $httpBackend.expect('DELETE', '/url', undefined, checkXSRF('secret')).respond('');        $http({url: '/url', method: 'GET'}); -      expect(headers['X-XSRF-TOKEN']).toBe('secret'); -        $http({url: '/url', method: 'POST', headers: {'S-ome': 'Header'}}); -      expect(headers['X-XSRF-TOKEN']).toBe('secret'); -        $http({url: '/url', method: 'PUT', headers: {'Another': 'Header'}}); -      expect(headers['X-XSRF-TOKEN']).toBe('secret'); -        $http({url: '/url', method: 'DELETE', headers: {}}); -      expect(headers['X-XSRF-TOKEN']).toBe('secret'); + +      $httpBackend.flush();      });    });    describe('short methods', function() { -    it('should have .get()', function() { -      $http.get('/url'); +    function checkHeader(name, value) { +      return function(headers) { +        return headers[name] == value; +      }; +    } -      expect(method).toBe('GET'); -      expect(url).toBe('/url'); +    it('should have get()', function() { +      $httpBackend.expect('GET', '/url').respond(''); +      $http.get('/url');      }); -    it('.get() should allow config param', function() { +    it('get() should allow config param', function() { +      $httpBackend.expect('GET', '/url', undefined, checkHeader('Custom', 'Header')).respond('');        $http.get('/url', {headers: {'Custom': 'Header'}}); - -      expect(method).toBe('GET'); -      expect(url).toBe('/url'); -      expect(headers['Custom']).toBe('Header');      }); -    it('should have .delete()', function() { +    it('should have delete()', function() { +      $httpBackend.expect('DELETE', '/url').respond('');        $http['delete']('/url'); - -      expect(method).toBe('DELETE'); -      expect(url).toBe('/url');      }); -    it('.delete() should allow config param', function() { +    it('delete() should allow config param', function() { +      $httpBackend.expect('DELETE', '/url', undefined, checkHeader('Custom', 'Header')).respond('');        $http['delete']('/url', {headers: {'Custom': 'Header'}}); - -      expect(method).toBe('DELETE'); -      expect(url).toBe('/url'); -      expect(headers['Custom']).toBe('Header');      }); -    it('should have .head()', function() { +    it('should have head()', function() { +      $httpBackend.expect('HEAD', '/url').respond('');        $http.head('/url'); - -      expect(method).toBe('HEAD'); -      expect(url).toBe('/url');      }); -    it('.head() should allow config param', function() { +    it('head() should allow config param', function() { +      $httpBackend.expect('HEAD', '/url', undefined, checkHeader('Custom', 'Header')).respond('');        $http.head('/url', {headers: {'Custom': 'Header'}}); - -      expect(method).toBe('HEAD'); -      expect(url).toBe('/url'); -      expect(headers['Custom']).toBe('Header');      }); -    it('should have .patch()', function() { +    it('should have patch()', function() { +      $httpBackend.expect('PATCH', '/url').respond('');        $http.patch('/url'); - -      expect(method).toBe('PATCH'); -      expect(url).toBe('/url');      }); -    it('.patch() should allow config param', function() { +    it('patch() should allow config param', function() { +      $httpBackend.expect('PATCH', '/url', undefined, checkHeader('Custom', 'Header')).respond('');        $http.patch('/url', {headers: {'Custom': 'Header'}}); - -      expect(method).toBe('PATCH'); -      expect(url).toBe('/url'); -      expect(headers['Custom']).toBe('Header');      }); -    it('should have .post()', function() { +    it('should have post()', function() { +      $httpBackend.expect('POST', '/url', 'some-data').respond('');        $http.post('/url', 'some-data'); - -      expect(method).toBe('POST'); -      expect(url).toBe('/url'); -      expect(data).toBe('some-data');      }); -    it('.post() should allow config param', function() { +    it('post() should allow config param', function() { +      $httpBackend.expect('POST', '/url', 'some-data', checkHeader('Custom', 'Header')).respond('');        $http.post('/url', 'some-data', {headers: {'Custom': 'Header'}}); - -      expect(method).toBe('POST'); -      expect(url).toBe('/url'); -      expect(data).toBe('some-data'); -      expect(headers['Custom']).toBe('Header');      }); -    it('should have .put()', function() { +    it('should have put()', function() { +      $httpBackend.expect('PUT', '/url', 'some-data').respond('');        $http.put('/url', 'some-data'); - -      expect(method).toBe('PUT'); -      expect(url).toBe('/url'); -      expect(data).toBe('some-data');      }); -    it('.put() should allow config param', function() { +    it('put() should allow config param', function() { +      $httpBackend.expect('PUT', '/url', 'some-data', checkHeader('Custom', 'Header')).respond('');        $http.put('/url', 'some-data', {headers: {'Custom': 'Header'}}); - -      expect(method).toBe('PUT'); -      expect(url).toBe('/url'); -      expect(data).toBe('some-data'); -      expect(headers['Custom']).toBe('Header');      }); -    it('should have .jsonp()', function() { +    it('should have jsonp()', function() { +      $httpBackend.expect('JSONP', '/url').respond('');        $http.jsonp('/url'); - -      expect(method).toBe('JSONP'); -      expect(url).toBe('/url');      }); -    it('.jsonp() should allow config param', function() { +    it('jsonp() should allow config param', function() { +      $httpBackend.expect('JSONP', '/url', undefined, checkHeader('Custom', 'Header')).respond('');        $http.jsonp('/url', {headers: {'Custom': 'Header'}}); - -      expect(method).toBe('JSONP'); -      expect(url).toBe('/url'); -      expect(headers['Custom']).toBe('Header');      });    }); @@ -454,7 +408,14 @@ describe('$http', function() {      describe('abort', function() { -      beforeEach(doCommonXhr); +      var future, rawXhrObject; + +      beforeEach(function() { +        $httpBackend.when('GET', '/url').then(''); +        future = $http({method: 'GET', url: '/url'}); +        rawXhrObject = MockXhr.$$lastInstance; +        spyOn(rawXhrObject, 'abort'); +      });        it('should return itself to allow chaining', function() {          expect(future.abort()).toBe(future); @@ -468,7 +429,7 @@ describe('$http', function() {        it('should not abort already finished request', function() { -        respond(200, 'content'); +        $httpBackend.flush();          future.abort();          expect(rawXhrObject.abort).not.toHaveBeenCalled(); @@ -478,31 +439,33 @@ describe('$http', function() {      describe('retry', function() { +      var future; + +      beforeEach(function() { +        $httpBackend.expect('HEAD', '/url-x').respond(''); +        future = $http({method: 'HEAD', url: '/url-x'}).on('2xx', callback); +      }); +        it('should retry last request with same callbacks', function() { -        doCommonXhr('HEAD', '/url-x'); -        respond(200, ''); -        $browser.xhr.reset(); -        onSuccess.reset(); +        $httpBackend.flush(); +        callback.reset(); +        $httpBackend.expect('HEAD', '/url-x').respond('');          future.retry(); -        expect($browser.xhr).toHaveBeenCalledOnce(); -        expect(method).toBe('HEAD'); -        expect(url).toBe('/url-x'); - -        respond(200, 'body'); -        expect(onSuccess).toHaveBeenCalledOnce(); +        $httpBackend.flush(); +        expect(callback).toHaveBeenCalledOnce();        });        it('should return itself to allow chaining', function() { -        doCommonXhr(); -        respond(200, ''); +        $httpBackend.flush(); + +        $httpBackend.expect('HEAD', '/url-x').respond('');          expect(future.retry()).toBe(future);        });        it('should throw error when pending request', function() { -        doCommonXhr();          expect(future.retry).toThrow('Can not retry request. Abort pending request first.');        });      }); @@ -510,98 +473,92 @@ describe('$http', function() {      describe('on', function() { -      var callback; +      var future; + +      function expectToMatch(status, pattern) { +        expectToNotMatch(status, pattern, true); +      } + +      function expectToNotMatch(status, pattern, match) { +        callback.reset(); +        future = $http({method: 'GET', url: '/' + status}); +        future.on(pattern, callback); +        $httpBackend.flush(); + +        if (match) expect(callback).toHaveBeenCalledOnce(); +        else expect(callback).not.toHaveBeenCalledOnce(); +      }        beforeEach(function() { -        future = $http({method: 'GET', url: '/url'}); -        callback = jasmine.createSpy('callback'); +        $httpBackend.when('GET').then(function(m, url) { +          return [parseInt(url.substr(1)), '', {}]; +        });        });        it('should return itself to allow chaining', function() { +        future = $http({method: 'GET', url: '/url'});          expect(future.on('200', noop)).toBe(future);        });        it('should call exact status code callback', function() { -        future.on('205', callback); -        respond(205, ''); - -        expect(callback).toHaveBeenCalledOnce(); +        expectToMatch(205, '205');        });        it('should match 2xx', function() { -        future.on('2xx', callback); - -        respond(200, ''); -        respond(201, ''); -        respond(266, ''); +        expectToMatch(200, '2xx'); +        expectToMatch(201, '2xx'); +        expectToMatch(266, '2xx'); -        respond(400, ''); -        respond(300, ''); - -        expect(callback).toHaveBeenCalled(); -        expect(callback.callCount).toBe(3); +        expectToNotMatch(400, '2xx'); +        expectToNotMatch(300, '2xx');        });        it('should match 20x', function() { -        future.on('20x', callback); - -        respond(200, ''); -        respond(201, ''); -        respond(205, ''); - -        respond(400, ''); -        respond(300, ''); -        respond(210, ''); -        respond(255, ''); +        expectToMatch(200, '20x'); +        expectToMatch(201, '20x'); +        expectToMatch(205, '20x'); -        expect(callback).toHaveBeenCalled(); -        expect(callback.callCount).toBe(3); +        expectToNotMatch(210, '20x'); +        expectToNotMatch(301, '20x'); +        expectToNotMatch(404, '20x'); +        expectToNotMatch(501, '20x');        });        it('should match 2x1', function() { -        future.on('2x1', callback); - -        respond(201, ''); -        respond(211, ''); -        respond(251, ''); - -        respond(400, ''); -        respond(300, ''); -        respond(210, ''); -        respond(255, ''); +        expectToMatch(201, '2x1'); +        expectToMatch(211, '2x1'); +        expectToMatch(251, '2x1'); -        expect(callback).toHaveBeenCalled(); -        expect(callback.callCount).toBe(3); +        expectToNotMatch(210, '2x1'); +        expectToNotMatch(301, '2x1'); +        expectToNotMatch(400, '2x1');        });        it('should match xxx', function() { -        future.on('xxx', callback); - -        respond(201, ''); -        respond(211, ''); -        respond(251, ''); -        respond(404, ''); -        respond(501, ''); - -        expect(callback).toHaveBeenCalled(); -        expect(callback.callCount).toBe(5); +        expectToMatch(200, 'xxx'); +        expectToMatch(210, 'xxx'); +        expectToMatch(301, 'xxx'); +        expectToMatch(406, 'xxx'); +        expectToMatch(510, 'xxx');        });        it('should call all matched callbacks', function() {          var no = jasmine.createSpy('wrong'); -        future.on('xxx', callback); -        future.on('2xx', callback); -        future.on('205', callback); -        future.on('3xx', no); -        future.on('2x1', no); -        future.on('4xx', no); -        respond(205, ''); +        $http({method: 'GET', url: '/205'}) +          .on('xxx', callback) +          .on('2xx', callback) +          .on('205', callback) +          .on('3xx', no) +          .on('2x1', no) +          .on('4xx', no); + +        $httpBackend.flush();          expect(callback).toHaveBeenCalled();          expect(callback.callCount).toBe(3); @@ -610,98 +567,66 @@ describe('$http', function() {        it('should allow list of status patterns', function() { -        future.on('2xx,3xx', callback); - -        respond(405, ''); -        expect(callback).not.toHaveBeenCalled(); - -        respond(201); -        expect(callback).toHaveBeenCalledOnce(); - -        respond(301); -        expect(callback.callCount).toBe(2); +        expectToMatch(201, '2xx,3xx'); +        expectToMatch(301, '2xx,3xx'); +        expectToNotMatch(405, '2xx,3xx');        });        it('should preserve the order of listeners', function() {          var log = ''; -        future.on('2xx', function() {log += '1';}); -        future.on('201', function() {log += '2';}); -        future.on('2xx', function() {log += '3';}); -        respond(201); +        $http({method: 'GET', url: '/201'}) +          .on('2xx', function() {log += '1';}) +          .on('201', function() {log += '2';}) +          .on('2xx', function() {log += '3';}); + +        $httpBackend.flush();          expect(log).toBe('123');        });        it('should know "success" alias', function() { -        future.on('success', callback); -        respond(200, ''); -        expect(callback).toHaveBeenCalledOnce(); - -        callback.reset(); -        respond(201, ''); -        expect(callback).toHaveBeenCalledOnce(); - -        callback.reset(); -        respond(250, ''); -        expect(callback).toHaveBeenCalledOnce(); +        expectToMatch(200, 'success'); +        expectToMatch(201, 'success'); +        expectToMatch(250, 'success'); -        callback.reset(); -        respond(404, ''); -        respond(501, ''); -        expect(callback).not.toHaveBeenCalled(); +        expectToNotMatch(403, 'success'); +        expectToNotMatch(501, 'success');        });        it('should know "error" alias', function() { -        future.on('error', callback); -        respond(401, ''); -        expect(callback).toHaveBeenCalledOnce(); - -        callback.reset(); -        respond(500, ''); -        expect(callback).toHaveBeenCalledOnce(); +        expectToMatch(401, 'error'); +        expectToMatch(500, 'error'); +        expectToMatch(0, 'error'); -        callback.reset(); -        respond(0, ''); -        expect(callback).toHaveBeenCalledOnce(); - -        callback.reset(); -        respond(201, ''); -        respond(200, ''); -        respond(300, ''); -        expect(callback).not.toHaveBeenCalled(); +        expectToNotMatch(201, 'error'); +        expectToNotMatch(200, 'error');        });        it('should know "always" alias', function() { -        future.on('always', callback); -        respond(201, ''); -        respond(200, ''); -        respond(300, ''); -        respond(401, ''); -        respond(502, ''); -        respond(0,   ''); -        respond(-1,  ''); -        respond(-2,  ''); - -        expect(callback).toHaveBeenCalled(); -        expect(callback.callCount).toBe(8); +        expectToMatch(200, 'always'); +        expectToMatch(201, 'always'); +        expectToMatch(250, 'always'); +        expectToMatch(300, 'always'); +        expectToMatch(302, 'always'); +        expectToMatch(404, 'always'); +        expectToMatch(501, 'always'); +        expectToMatch(0, 'always'); +        expectToMatch(-1, 'always'); +        expectToMatch(-2, 'always');        });        it('should call "xxx" when 0 status code', function() { -        future.on('xxx', callback); -        respond(0, ''); -        expect(callback).toHaveBeenCalledOnce(); +        expectToMatch(0, 'xxx');        });        it('should not call "2xx" when 0 status code', function() { -        future.on('2xx', callback); -        respond(0, ''); -        expect(callback).not.toHaveBeenCalled(); +        expectToNotMatch(0, '2xx');        });        it('should normalize internal statuses -1, -2 to 0', function() { @@ -709,36 +634,27 @@ describe('$http', function() {            expect(status).toBe(0);          }); -        future.on('xxx', callback); -        respond(-1, ''); -        respond(-2, ''); +        $http({method: 'GET', url: '/0'}).on('xxx', callback); +        $http({method: 'GET', url: '/-1'}).on('xxx', callback); +        $http({method: 'GET', url: '/-2'}).on('xxx', callback); +        $httpBackend.flush();          expect(callback).toHaveBeenCalled(); -        expect(callback.callCount).toBe(2); +        expect(callback.callCount).toBe(3);        });        it('should match "timeout" when -1 internal status', function() { -        future.on('timeout', callback); -        respond(-1, ''); - -        expect(callback).toHaveBeenCalledOnce(); +        expectToMatch(-1, 'timeout');        });        it('should match "abort" when 0 status', function() { -        future.on('abort', callback); -        respond(0, ''); - -        expect(callback).toHaveBeenCalledOnce(); +        expectToMatch(0, 'abort');        });        it('should match "error" when 0, -1, or -2', function() { -        future.on('error', callback); -        respond(0,  ''); -        respond(-1, ''); -        respond(-2, ''); - -        expect(callback).toHaveBeenCalled(); -        expect(callback.callCount).toBe(3); +        expectToMatch(0, 'error'); +        expectToMatch(-1, 'error'); +        expectToMatch(-2, 'error');        });      });    }); @@ -746,29 +662,28 @@ describe('$http', function() {    describe('scope.$apply', function() { -    beforeEach(doCommonXhr); -      it('should $apply after success callback', function() { -      respond(200, ''); +      $httpBackend.when('GET').then(200); +      $http({method: 'GET', url: '/some'}); +      $httpBackend.flush();        expect(scope.$apply).toHaveBeenCalledOnce();      });      it('should $apply after error callback', function() { -      respond(404, ''); +      $httpBackend.when('GET').then(404); +      $http({method: 'GET', url: '/some'}); +      $httpBackend.flush();        expect(scope.$apply).toHaveBeenCalledOnce();      });      it('should $apply even if exception thrown during callback', function() { -      onSuccess.andThrow('error in callback'); -      onError.andThrow('error in callback'); - -      respond(200, ''); -      expect(scope.$apply).toHaveBeenCalledOnce(); +      $httpBackend.when('GET').then(200); +      callback.andThrow('error in callback'); -      scope.$apply.reset(); -      respond(400, ''); +      $http({method: 'GET', url: '/some'}).on('200', callback); +      $httpBackend.flush();        expect(scope.$apply).toHaveBeenCalledOnce();        $exceptionHandler.errors = []; @@ -783,14 +698,14 @@ describe('$http', function() {        describe('default', function() {          it('should transform object into json', function() { +          $httpBackend.expect('POST', '/url', '{"one":"two"}').respond('');            $http({method: 'POST', url: '/url', data: {one: 'two'}}); -          expect(data).toBe('{"one":"two"}');          });          it('should ignore strings', function() { +          $httpBackend.expect('POST', '/url', 'string-data').respond('');            $http({method: 'POST', url: '/url', data: 'string-data'}); -          expect(data).toBe('string-data');          });        });      }); @@ -801,40 +716,47 @@ describe('$http', function() {        describe('default', function() {          it('should deserialize json objects', function() { -          doCommonXhr(); -          respond(200, '{"foo":"bar","baz":23}'); +          $httpBackend.expect('GET', '/url').respond('{"foo":"bar","baz":23}'); +          $http({method: 'GET', url: '/url'}).on('200', callback); +          $httpBackend.flush(); -          expect(onSuccess.mostRecentCall.args[0]).toEqual({foo: 'bar', baz: 23}); +          expect(callback).toHaveBeenCalledOnce(); +          expect(callback.mostRecentCall.args[0]).toEqual({foo: 'bar', baz: 23});          });          it('should deserialize json arrays', function() { -          doCommonXhr(); -          respond(200, '[1, "abc", {"foo":"bar"}]'); +          $httpBackend.expect('GET', '/url').respond('[1, "abc", {"foo":"bar"}]'); +          $http({method: 'GET', url: '/url'}).on('200', callback); +          $httpBackend.flush(); -          expect(onSuccess.mostRecentCall.args[0]).toEqual([1, 'abc', {foo: 'bar'}]); +          expect(callback).toHaveBeenCalledOnce(); +          expect(callback.mostRecentCall.args[0]).toEqual([1, 'abc', {foo: 'bar'}]);          });          it('should deserialize json with security prefix', function() { -          doCommonXhr(); -          respond(200, ')]}\',\n[1, "abc", {"foo":"bar"}]'); +          $httpBackend.expect('GET', '/url').respond(')]}\',\n[1, "abc", {"foo":"bar"}]'); +          $http({method: 'GET', url: '/url'}).on('200', callback); +          $httpBackend.flush(); -          expect(onSuccess.mostRecentCall.args[0]).toEqual([1, 'abc', {foo:'bar'}]); +          expect(callback).toHaveBeenCalledOnce(); +          expect(callback.mostRecentCall.args[0]).toEqual([1, 'abc', {foo:'bar'}]);          });        }); +        it('should pipeline more functions', function() {          function first(d) {return d + '1';}          function second(d) {return d + '2';} -        onSuccess = jasmine.createSpy('onSuccess'); -        $http({method: 'POST', url: '/url', data: '0', transformResponse: [first, second]}) -          .on('200', onSuccess); +        $httpBackend.expect('POST', '/url').respond('0'); +        $http({method: 'POST', url: '/url', transformResponse: [first, second]}) +          .on('200', callback); +        $httpBackend.flush(); -        respond(200, '0'); -        expect(onSuccess).toHaveBeenCalledOnce(); -        expect(onSuccess.mostRecentCall.args[0]).toBe('012'); +        expect(callback).toHaveBeenCalledOnce(); +        expect(callback.mostRecentCall.args[0]).toBe('012');        });      });    }); @@ -842,95 +764,100 @@ describe('$http', function() {    describe('cache', function() { -    function doFirstCacheRequest(method, responseStatus) { -      onSuccess = jasmine.createSpy('on200'); -      $http({method: method || 'get', url: '/url', cache: true}); -      respond(responseStatus || 200, 'content'); -      $browser.xhr.reset(); +    function doFirstCacheRequest(method, respStatus, headers) { +      $httpBackend.expect(method || 'GET', '/url').respond(respStatus || 200, 'content', headers); +      $http({method: method || 'GET', url: '/url', cache: true}); +      $httpBackend.flush();      }      it('should cache GET request', function() {        doFirstCacheRequest(); -      $http({method: 'get', url: '/url', cache: true}).on('200', onSuccess); +      $http({method: 'get', url: '/url', cache: true}).on('200', callback);        $browser.defer.flush(); -      expect(onSuccess).toHaveBeenCalledOnce(); -      expect(onSuccess.mostRecentCall.args[0]).toBe('content'); -      expect($browser.xhr).not.toHaveBeenCalled(); +      expect(callback).toHaveBeenCalledOnce(); +      expect(callback.mostRecentCall.args[0]).toBe('content');      });      it('should always call callback asynchronously', function() {        doFirstCacheRequest(); +      $http({method: 'get', url: '/url', cache: true}).on('200', callback); -      $http({method: 'get', url: '/url', cache: true}).on('200', onSuccess); -      expect(onSuccess).not.toHaveBeenCalled(); +      expect(callback).not.toHaveBeenCalledOnce();      });      it('should not cache POST request', function() { -      doFirstCacheRequest('post'); +      doFirstCacheRequest('POST'); -      $http({method: 'post', url: '/url', cache: true}).on('200', onSuccess); -      $browser.defer.flush(); -      expect(onSuccess).not.toHaveBeenCalled(); -      expect($browser.xhr).toHaveBeenCalledOnce(); +      $httpBackend.expect('POST', '/url').respond('content2'); +      $http({method: 'POST', url: '/url', cache: true}).on('200', callback); +      $httpBackend.flush(); + +      expect(callback).toHaveBeenCalledOnce(); +      expect(callback.mostRecentCall.args[0]).toBe('content2');      });      it('should not cache PUT request', function() { -      doFirstCacheRequest('put'); +      doFirstCacheRequest('PUT'); -      $http({method: 'put', url: '/url', cache: true}).on('200', onSuccess); -      $browser.defer.flush(); -      expect(onSuccess).not.toHaveBeenCalled(); -      expect($browser.xhr).toHaveBeenCalledOnce(); +      $httpBackend.expect('PUT', '/url').respond('content2'); +      $http({method: 'PUT', url: '/url', cache: true}).on('200', callback); +      $httpBackend.flush(); + +      expect(callback).toHaveBeenCalledOnce(); +      expect(callback.mostRecentCall.args[0]).toBe('content2');      });      it('should not cache DELETE request', function() { -      doFirstCacheRequest('delete'); +      doFirstCacheRequest('DELETE'); -      $http({method: 'delete', url: '/url', cache: true}).on('200', onSuccess); -      $browser.defer.flush(); -      expect(onSuccess).not.toHaveBeenCalled(); -      expect($browser.xhr).toHaveBeenCalledOnce(); +      $httpBackend.expect('DELETE', '/url').respond(206); +      $http({method: 'DELETE', url: '/url', cache: true}).on('206', callback); +      $httpBackend.flush(); + +      expect(callback).toHaveBeenCalledOnce();      });      it('should not cache non 2xx responses', function() { -      doFirstCacheRequest('get', 404); +      doFirstCacheRequest('GET', 404); -      $http({method: 'get', url: '/url', cache: true}).on('200', onSuccess); -      $browser.defer.flush(); -      expect(onSuccess).not.toHaveBeenCalled(); -      expect($browser.xhr).toHaveBeenCalledOnce(); +      $httpBackend.expect('GET', '/url').respond('content2'); +      $http({method: 'GET', url: '/url', cache: true}).on('200', callback); +      $httpBackend.flush(); + +      expect(callback).toHaveBeenCalledOnce(); +      expect(callback.mostRecentCall.args[0]).toBe('content2');      });      it('should cache the headers as well', function() { -      doFirstCacheRequest(); -      onSuccess.andCallFake(function(r, s, headers) { +      doFirstCacheRequest('GET', 200, {'content-encoding': 'gzip', 'server': 'Apache'}); +      callback.andCallFake(function(r, s, headers) {          expect(headers()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'});          expect(headers('server')).toBe('Apache');        }); -      $http({method: 'get', url: '/url', cache: true}).on('200', onSuccess); +      $http({method: 'GET', url: '/url', cache: true}).on('200', callback);        $browser.defer.flush(); -      expect(onSuccess).toHaveBeenCalledOnce(); +      expect(callback).toHaveBeenCalledOnce();      });      it('should cache status code as well', function() { -      doFirstCacheRequest('get', 201); -      onSuccess.andCallFake(function(r, status, h) { +      doFirstCacheRequest('GET', 201); +      callback.andCallFake(function(r, status, h) {          expect(status).toBe(201);        }); -      $http({method: 'get', url: '/url', cache: true}).on('2xx', onSuccess); +      $http({method: 'get', url: '/url', cache: true}).on('2xx', callback);        $browser.defer.flush(); -      expect(onSuccess).toHaveBeenCalledOnce(); +      expect(callback).toHaveBeenCalledOnce();      });    }); @@ -938,29 +865,34 @@ describe('$http', function() {    describe('pendingCount', function() {      it('should return number of pending requests', function() { +      $httpBackend.when('GET').then(200);        expect($http.pendingCount()).toBe(0);        $http({method: 'get', url: '/some'});        expect($http.pendingCount()).toBe(1); -      respond(200, ''); +      $httpBackend.flush();        expect($http.pendingCount()).toBe(0);      });      it('should decrement the counter when request aborted', function() { +      $httpBackend.when('GET').then(0);        future = $http({method: 'get', url: '/x'});        expect($http.pendingCount()).toBe(1); +        future.abort(); -      respond(0, ''); +      $httpBackend.flush();        expect($http.pendingCount()).toBe(0);      });      it('should decrement the counter when served from cache', function() { +      $httpBackend.when('GET').then(200); +        $http({method: 'get', url: '/cached', cache: true}); -      respond(200, 'content'); +      $httpBackend.flush();        expect($http.pendingCount()).toBe(0);        $http({method: 'get', url: '/cached', cache: true}); @@ -972,12 +904,13 @@ describe('$http', function() {      it('should decrement the counter before firing callbacks', function() { -      $http({method: 'get', url: '/cached'}).on('xxx', function() { +      $httpBackend.when('GET').then(200); +      $http({method: 'get', url: '/url'}).on('xxx', function() {          expect($http.pendingCount()).toBe(0);        });        expect($http.pendingCount()).toBe(1); -      respond(200, 'content'); +      $httpBackend.flush();      });    });  }); diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index 2ddb26e1..c3bc1333 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -134,14 +134,13 @@ describe("widget", function() {        expect($rootScope.$$childHead).toBeFalsy();      })); -    it('should do xhr request and cache it', inject(function($rootScope, $browser, $compile) { +    it('should do xhr request and cache it', inject(function($rootScope, $httpBackend, $compile) {        var element = $compile('<ng:include src="url"></ng:include>')($rootScope); -      var $browserXhr = $browser.xhr; -      $browserXhr.expectGET('myUrl').respond('my partial'); +      $httpBackend.expect('GET', 'myUrl').respond('my partial');        $rootScope.url = 'myUrl';        $rootScope.$digest(); -      $browserXhr.flush(); +      $httpBackend.flush();        expect(element.text()).toEqual('my partial');        $rootScope.url = null; @@ -155,14 +154,13 @@ describe("widget", function() {      }));      it('should clear content when error during xhr request', -        inject(function($browser, $compile, $rootScope) { +        inject(function($httpBackend, $compile, $rootScope) {        var element = $compile('<ng:include src="url">content</ng:include>')($rootScope); -      var $browserXhr = $browser.xhr; -      $browserXhr.expectGET('myUrl').respond(404, ''); +      $httpBackend.expect('GET', 'myUrl').respond(404, '');        $rootScope.url = 'myUrl';        $rootScope.$digest(); -      $browserXhr.flush(); +      $httpBackend.flush();        expect(element.text()).toBe('');      })); @@ -500,33 +498,33 @@ describe("widget", function() {      it('should load content via xhr when route changes', -        inject(function($rootScope, $compile, $browser, $location, $route) { +        inject(function($rootScope, $compile, $httpBackend, $location, $route) {        $route.when('/foo', {template: 'myUrl1'});        $route.when('/bar', {template: 'myUrl2'});        expect(element.text()).toEqual('');        $location.path('/foo'); -      $browser.xhr.expectGET('myUrl1').respond('<div>{{1+3}}</div>'); +      $httpBackend.expect('GET', 'myUrl1').respond('<div>{{1+3}}</div>');        $rootScope.$digest(); -      $browser.xhr.flush(); +      $httpBackend.flush();        expect(element.text()).toEqual('4');        $location.path('/bar'); -      $browser.xhr.expectGET('myUrl2').respond('angular is da best'); +      $httpBackend.expect('GET', 'myUrl2').respond('angular is da best');        $rootScope.$digest(); -      $browser.xhr.flush(); +      $httpBackend.flush();        expect(element.text()).toEqual('angular is da best');      }));      it('should remove all content when location changes to an unknown route', -        inject(function($rootScope, $compile, $location, $browser, $route) { +        inject(function($rootScope, $compile, $location, $httpBackend, $route) {        $route.when('/foo', {template: 'myUrl1'});        $location.path('/foo'); -      $browser.xhr.expectGET('myUrl1').respond('<div>{{1+3}}</div>'); +      $httpBackend.expect('GET', 'myUrl1').respond('<div>{{1+3}}</div>');        $rootScope.$digest(); -      $browser.xhr.flush(); +      $httpBackend.flush();        expect(element.text()).toEqual('4');        $location.path('/unknown'); @@ -535,14 +533,14 @@ describe("widget", function() {      }));      it('should chain scopes and propagate evals to the child scope', -        inject(function($rootScope, $compile, $location, $browser, $route) { +        inject(function($rootScope, $compile, $location, $httpBackend, $route) {        $route.when('/foo', {template: 'myUrl1'});        $rootScope.parentVar = 'parent';        $location.path('/foo'); -      $browser.xhr.expectGET('myUrl1').respond('<div>{{parentVar}}</div>'); +      $httpBackend.expect('GET', 'myUrl1').respond('<div>{{parentVar}}</div>');        $rootScope.$digest(); -      $browser.xhr.flush(); +      $httpBackend.flush();        expect(element.text()).toEqual('parent');        $rootScope.parentVar = 'new parent'; @@ -551,10 +549,11 @@ describe("widget", function() {      }));      it('should be possible to nest ng:view in ng:include', inject(function() { +      // TODO(vojta): refactor this test        var injector = angular.injector('ng', 'ngMock');        var myApp = injector.get('$rootScope'); -      var $browser = injector.get('$browser'); -      $browser.xhr.expectGET('includePartial.html').respond('view: <ng:view></ng:view>'); +      var $httpBackend = injector.get('$httpBackend'); +      $httpBackend.expect('GET', 'includePartial.html').respond('view: <ng:view></ng:view>');        injector.get('$location').path('/foo');        var $route = injector.get('$route'); @@ -566,9 +565,10 @@ describe("widget", function() {            '</div>')(myApp);        myApp.$apply(); -      $browser.xhr.expectGET('viewPartial.html').respond('content'); +      $httpBackend.expect('GET', 'viewPartial.html').respond('content'); +      $httpBackend.flush();        myApp.$digest(); -      $browser.xhr.flush(); +      $httpBackend.flush();        expect(myApp.$element.text()).toEqual('include: view: content');        expect($route.current.template).toEqual('viewPartial.html'); @@ -576,11 +576,10 @@ describe("widget", function() {      }));      it('should initialize view template after the view controller was initialized even when ' + -       'templates were cached', inject(function($rootScope, $compile, $location, $browser, $route) { +       'templates were cached', inject(function($rootScope, $compile, $location, $httpBackend, $route) {        //this is a test for a regression that was introduced by making the ng:view cache sync        $route.when('/foo', {controller: ParentCtrl, template: 'viewPartial.html'}); -        $rootScope.log = [];        function ParentCtrl() { @@ -592,12 +591,12 @@ describe("widget", function() {        };        $location.path('/foo'); -      $browser.xhr.expectGET('viewPartial.html'). +      $httpBackend.expect('GET', 'viewPartial.html').            respond('<div ng:init="log.push(\'init\')">' +                      '<div ng:controller="ChildCtrl"></div>' +                    '</div>');        $rootScope.$apply(); -      $browser.xhr.flush(); +      $httpBackend.flush();        expect($rootScope.log).toEqual(['parent', 'init', 'child']); @@ -608,13 +607,12 @@ describe("widget", function() {        $rootScope.log = [];        $location.path('/foo');        $rootScope.$apply(); -      $browser.defer.flush();        expect($rootScope.log).toEqual(['parent', 'init', 'child']);      }));      it('should discard pending xhr callbacks if a new route is requested before the current ' + -        'finished loading', inject(function($route, $rootScope, $location, $browser) { +        'finished loading', inject(function($route, $rootScope, $location, $httpBackend) {        // this is a test for a bad race condition that affected feedback        $route.when('/foo', {template: 'myUrl1'}); @@ -623,26 +621,26 @@ describe("widget", function() {        expect($rootScope.$element.text()).toEqual('');        $location.path('/foo'); -      $browser.xhr.expectGET('myUrl1').respond('<div>{{1+3}}</div>'); +      $httpBackend.expect('GET', 'myUrl1').respond('<div>{{1+3}}</div>');        $rootScope.$digest();        $location.path('/bar'); -      $browser.xhr.expectGET('myUrl2').respond('<div>{{1+1}}</div>'); +      $httpBackend.expect('GET', 'myUrl2').respond('<div>{{1+1}}</div>');        $rootScope.$digest(); -      $browser.xhr.flush(); // now that we have to requests pending, flush! +      $httpBackend.flush(); // now that we have to requests pending, flush!        expect($rootScope.$element.text()).toEqual('2');      }));      it('should clear the content when error during xhr request', -        inject(function($route, $location, $rootScope, $browser) { +        inject(function($route, $location, $rootScope, $httpBackend) {        $route.when('/foo', {controller: noop, template: 'myUrl1'});        $location.path('/foo'); -      $browser.xhr.expectGET('myUrl1').respond(404, ''); +      $httpBackend.expect('GET', 'myUrl1').respond(404, '');        $rootScope.$element.text('content');        $rootScope.$digest(); -      $browser.xhr.flush(); +      $httpBackend.flush();        expect($rootScope.$element.text()).toBe('');      }));  | 
