diff options
| -rw-r--r-- | src/angular-mocks.js | 57 | ||||
| -rw-r--r-- | src/service/http.js | 680 | ||||
| -rw-r--r-- | src/service/httpBackend.js | 40 | ||||
| -rw-r--r-- | test/angular-mocksSpec.js | 288 | ||||
| -rw-r--r-- | test/service/httpBackendSpec.js | 23 | ||||
| -rw-r--r-- | test/service/httpSpec.js | 53 | ||||
| -rw-r--r-- | test/widgetsSpec.js | 9 |
7 files changed, 533 insertions, 617 deletions
diff --git a/src/angular-mocks.js b/src/angular-mocks.js index 16be7038..b4a1cbbc 100644 --- a/src/angular-mocks.js +++ b/src/angular-mocks.js @@ -48,9 +48,7 @@ angular.module.ngMock.$BrowserProvider = function(){ }; }; angular.module.ngMock.$Browser = function() { - var self = this, - expectations = {}, - requests = []; + var self = this; this.isMock = true; self.$$url = "http://server"; @@ -590,6 +588,10 @@ angular.module.ngMock.dump = function(object){ /** * @ngdoc object * @name angular.module.ngMock.$httpBackend + * @describe + * Fake HTTP backend used by the $http service during testing. This implementation can be used to + * respond with static or dynamic responses via the `expect` and `when` apis and their shortcuts + * (`expectGET`, `whenPOST`, etc). */ angular.module.ngMock.$HttpBackendProvider = function() { this.$get = function() { @@ -598,7 +600,13 @@ angular.module.ngMock.$HttpBackendProvider = function() { responses = []; function createResponse(status, data, headers) { - return angular.isNumber(status) ? [status, data, headers] : [200, status, data]; + if (isFunction(status)) return status; + + return function() { + return angular.isNumber(status) + ? [status, data, headers] + : [200, status, data]; + } } // TODO(vojta): change params to: method, url, data, headers, callback @@ -608,28 +616,29 @@ angular.module.ngMock.$HttpBackendProvider = function() { wasExpected = false; function prettyPrint(data) { - if (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp) - return data; - return angular.toJson(data); + return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp) + ? data + : angular.toJson(data); } if (expectation && expectation.match(method, url)) { if (!expectation.matchData(data)) throw Error('Expected ' + expectation + ' with different data\n' + - 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data); + 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data); if (!expectation.matchHeaders(headers)) throw Error('Expected ' + expectation + ' with different headers\n' + - 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + prettyPrint(headers)); + 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + prettyPrint(headers)); expectations.shift(); if (expectation.response) { responses.push(function() { - xhr.$$headers = expectation.response[2]; - callback(expectation.response[0], expectation.response[1]); + var response = expectation.response(method, url, data, headers); + xhr.$$respHeaders = response[2]; + callback(response[0], response[1], xhr.getAllResponseHeaders()); }); - return method == 'JSONP' ? undefined : xhr; + return; } wasExpected = true; } @@ -639,12 +648,11 @@ angular.module.ngMock.$HttpBackendProvider = function() { 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]); + var response = definition.response(method, url, data, headers); + xhr.$$respHeaders = response[2]; + callback(response[0], response[1], xhr.getAllResponseHeaders()); }); - return method == 'JSONP' ? undefined : xhr; + return; } } throw wasExpected ? @@ -658,7 +666,7 @@ angular.module.ngMock.$HttpBackendProvider = function() { definitions.push(definition); return { respond: function(status, data, headers) { - definition.response = angular.isFunction(status) ? status : createResponse(status, data, headers); + definition.response = createResponse(status, data, headers); } }; }; @@ -756,7 +764,8 @@ function MockXhr() { this.$$method = method; this.$$url = url; this.$$async = async; - this.$$headers = {}; + this.$$reqHeaders = {}; + this.$$respHeaders = {}; }; this.send = function(data) { @@ -764,20 +773,20 @@ function MockXhr() { }; this.setRequestHeader = function(key, value) { - this.$$headers[key] = value; + this.$$reqHeaders[key] = value; }; this.getResponseHeader = function(name) { // the lookup must be case insensitive, that's why we try two quick lookups and full scan at last - var header = this.$$headers[name]; + var header = this.$$respHeaders[name]; if (header) return header; name = angular.lowercase(name); - header = this.$$headers[name]; + header = this.$$respHeaders[name]; if (header) return header; header = undefined; - angular.forEach(this.$$headers, function(headerVal, headerName) { + angular.forEach(this.$$respHeaders, function(headerVal, headerName) { if (!header && angular.lowercase(headerName) == name) header = headerVal; }); return header; @@ -786,7 +795,7 @@ function MockXhr() { this.getAllResponseHeaders = function() { var lines = []; - angular.forEach(this.$$headers, function(value, key) { + angular.forEach(this.$$respHeaders, function(value, key) { lines.push(key + ': ' + value); }); return lines.join('\n'); diff --git a/src/service/http.js b/src/service/http.js index e6a42b65..bd8e6e65 100644 --- a/src/service/http.js +++ b/src/service/http.js @@ -4,11 +4,13 @@ * Parse headers into key value object * * @param {string} headers Raw headers as a string - * @returns {Object} Parsed headers as key valu object + * @returns {Object} Parsed headers as key value object */ function parseHeaders(headers) { var parsed = {}, key, val, i; + if (!headers) return parsed; + forEach(headers.split('\n'), function(line) { i = line.indexOf(':'); key = lowercase(trim(line.substr(0, i))); @@ -26,6 +28,34 @@ function parseHeaders(headers) { return parsed; } + +/** + * Returns a function that provides access to parsed headers. + * + * Headers are lazy parsed when first requested. + * @see parseHeaders + * + * @param {(string|Object)} headers Headers to provide access to. + * @returns {function(string=)} Returns a getter function which if called with: + * + * - if called with single an argument returns a single header value or null + * - if called with no arguments returns an object containing all headers. + */ +function headersGetter(headersString) { + var headers = isObject(headersString) ? headersString : undefined; + + return function(name) { + if (!headers) headers = parseHeaders(headersString); + + if (name) { + return headers[lowercase(name)] || null; + } + + return headers; + }; +} + + /** * Chain all given functions * @@ -36,7 +66,7 @@ function parseHeaders(headers) { * @param {*=} param Optional parameter to be passed to all transform functions. * @returns {*} Transformed data. */ -function transform(data, fns, param) { +function transformData(data, fns, param) { if (isFunction(fns)) return fns(data); @@ -48,13 +78,18 @@ function transform(data, fns, param) { } +function isSuccess(status) { + return 200 <= status && status < 300; +} + + function $HttpProvider() { var JSON_START = /^\s*(\[|\{[^\{])/, JSON_END = /[\}\]]\s*$/, PROTECTION_PREFIX = /^\)\]\}',?\n/; var $config = this.defaults = { - // transform in-coming reponse data + // transform incoming response data transformResponse: function(data) { if (isString(data)) { // strip json vulnerability protection prefix @@ -65,7 +100,7 @@ function $HttpProvider() { return data; }, - // transform out-going request data + // transform outgoing request data transformRequest: function(d) { return isObject(d) ? toJson(d) : d; }, @@ -81,431 +116,328 @@ function $HttpProvider() { } }; - var responseInterceptors = this.responseInterceptors = []; + var providerResponseInterceptors = this.responseInterceptors = []; this.$get = ['$httpBackend', '$browser', '$exceptionHandler', '$cacheFactory', '$rootScope', '$q', '$injector', function($httpBackend, $browser, $exceptionHandler, $cacheFactory, $rootScope, $q, $injector) { - var defaultCache = $cacheFactory('$http'); + var defaultCache = $cacheFactory('$http'), + responseInterceptors = []; - forEach(responseInterceptors, function(interceptor, index) { - if (isString(interceptor)) { - responseInterceptors[index] = $injector.get(interceptor); - } - }); + forEach(providerResponseInterceptors, function(interceptor) { + responseInterceptors.push(isString(interceptor) ? $injector.get(interceptor) : interceptor); + }); - /** - * @ngdoc function - * @name angular.module.ng.$http - * @requires $httpBacked - * @requires $browser - * @requires $exceptionHandler - * @requires $cacheFactory - * - * @param {object} config Object describing the request to be made and how it should be processed. - * The object has following properties: - * - * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) - * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. - * - **data** – `{string|Object}` – Data to be sent as the request message data. - * - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server. - * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the - * GET request, otherwise if a cache instance built with $cacheFactory, this cache will be - * used for caching. - * - * @returns {HttpPromise} Returns a promise object with the standard `then` method and two http - * specific methods: `success` and `error`. The `then` method takes two arguments a success and - * an error callback which will be called with a response object. The `success` and `error` - * methods take a single argument - a function that will be called when the request succeeds or - * fails respectively. The arguments passed into these functions are destructured representation - * of the response object passed into the `then` method. The response object has these - * properties: - * - * - **data** – `{string|Object}` – The response body transformed with the transform functions. - * - **status** – `{number}` – HTTP status code of the response. - * - **headers** – `{function([headerName])}` – Header getter function. - * - **config** – `{Object}` – The configuration object that was used to generate the request. - * - * @property {Array.<Object>} pendingRequests Array of config objects for pending requests. - * This is primarily meant to be used for debugging purposes. - * - * @description - * $http is a service through which XHR and JSONP requests can be made. - */ - function $http(config) { - var req = new XhrFuture().send(config), - deferredResp = $q.defer(), - promise = deferredResp.promise; - - forEach(responseInterceptors, function(interceptor) { - promise = interceptor(promise); - }); + /** + * @ngdoc function + * @name angular.module.ng.$http + * @requires $httpBacked + * @requires $browser + * @requires $exceptionHandler //TODO(i): still needed? + * @requires $cacheFactory + * + * @param {object} config Object describing the request to be made and how it should be processed. + * The object has following properties: + * + * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) + * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. + * - **data** – `{string|Object}` – Data to be sent as the request message data. + * - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server. + * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the + * GET request, otherwise if a cache instance built with $cacheFactory, this cache will be + * used for caching. + * + * @returns {HttpPromise} Returns a promise object with the standard `then` method and two http + * specific methods: `success` and `error`. The `then` method takes two arguments a success and + * an error callback which will be called with a response object. The `success` and `error` + * methods take a single argument - a function that will be called when the request succeeds or + * fails respectively. The arguments passed into these functions are destructured representation + * of the response object passed into the `then` method. The response object has these + * properties: + * + * - **data** – `{string|Object}` – The response body transformed with the transform functions. + * - **status** – `{number}` – HTTP status code of the response. + * - **headers** – `{function([headerName])}` – Header getter function. + * - **config** – `{Object}` – The configuration object that was used to generate the request. + * + * @property {Array.<Object>} pendingRequests Array of config objects for pending requests. + * This is primarily meant to be used for debugging purposes. + * + * @description + * $http is a service through which XHR and JSONP requests can be made. + */ + function $http(config) { + config.method = uppercase(config.method); - promise.success = function(fn) { - promise.then(function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; + var reqTransformFn = config.transformRequest || $config.transformRequest, + respTransformFn = config.transformResponse || $config.transformResponse, + reqData = transformData(config.data, reqTransformFn), + defHeaders = $config.headers, + reqHeaders = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']}, + defHeaders.common, defHeaders[lowercase(config.method)], config.headers), + promise; - promise.error = function(fn) { - promise.then(null, function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; - req.on('success', function(data, status, headers) { - deferredResp.resolve({data: data, status: status, headers: headers, config: config}); - }).on('error', function(data, status, headers) { - deferredResp.reject({data: data, status: status, headers: headers, config: config}); - }); + // send request + promise = sendReq(config, reqData, reqHeaders); + - return promise; - } - - $http.pendingRequests = []; - - /** - * @ngdoc method - * @name angular.module.ng.$http#get - * @methodOf angular.module.ng.$http - * - * @description - * Shortcut method to perform `GET` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name angular.module.ng.$http#delete - * @methodOf angular.module.ng.$http - * - * @description - * Shortcut method to perform `DELETE` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name angular.module.ng.$http#head - * @methodOf angular.module.ng.$http - * - * @description - * Shortcut method to perform `HEAD` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {XhrFuture} Future object - */ - - /** - * @ngdoc method - * @name angular.module.ng.$http#patch - * @methodOf angular.module.ng.$http - * - * @description - * Shortcut method to perform `PATCH` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name angular.module.ng.$http#jsonp - * @methodOf angular.module.ng.$http - * - * @description - * Shortcut method to perform `JSONP` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request. - * Should contain `JSON_CALLBACK` string. - * @param {Object=} config Optional configuration object - * @returns {XhrFuture} Future object - */ - createShortMethods('get', 'delete', 'head', 'patch', 'jsonp'); - - /** - * @ngdoc method - * @name angular.module.ng.$http#post - * @methodOf angular.module.ng.$http - * - * @description - * Shortcut method to perform `POST` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name angular.module.ng.$http#put - * @methodOf angular.module.ng.$http - * - * @description - * Shortcut method to perform `PUT` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {XhrFuture} Future object - */ - createShortMethodsWithData('post', 'put'); - - return $http; - - function createShortMethods(names) { - forEach(arguments, function(name) { - $http[name] = function(url, config) { - return $http(extend(config || {}, { - method: name, - url: url - })); + // transform future response + promise = promise.then(transformResponse, transformResponse); + + // apply interceptors + forEach(responseInterceptors, function(interceptor) { + promise = interceptor(promise); + }); + + promise.success = function(fn) { + promise.then(function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; }; - }); - } - - function createShortMethodsWithData(name) { - forEach(arguments, function(name) { - $http[name] = function(url, data, config) { - return $http(extend(config || {}, { - method: name, - url: url, - data: data - })); + + promise.error = function(fn) { + promise.then(null, function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; }; - }); - } - - /** - * Represents Request object, returned by $http() - * - * !!! ACCESSES CLOSURE VARS: - * $httpBackend, $browser, $config, $log, $rootScope, defaultCache, $http.pendingRequests - */ - function XhrFuture() { - var rawRequest, parsedHeaders, - cfg = {}, callbacks = [], - defHeaders = $config.headers, - self = this; + + return promise; + + function transformResponse(response) { + // make a copy since the response must be cacheable + var resp = extend({}, response, { + data: transformData(response.data, respTransformFn, response.headers) + }); + return (isSuccess(response.status)) + ? resp + : $q.reject(resp); + } + } + + $http.pendingRequests = []; /** - * Callback registered to $httpBackend(): - * - caches the response if desired - * - calls fireCallbacks() - * - clears the reference to raw request object + * @ngdoc method + * @name angular.module.ng.$http#get + * @methodOf angular.module.ng.$http + * + * @description + * Shortcut method to perform `GET` request + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object */ - function done(status, response) { - // aborted request or jsonp - if (!rawRequest) parsedHeaders = {}; - - if (cfg.cache && cfg.method == 'GET') { - var cache = isObject(cfg.cache) && cfg.cache || defaultCache; - if (200 <= status && status < 300) { - parsedHeaders = parsedHeaders || parseHeaders(rawRequest.getAllResponseHeaders()); - cache.put(cfg.url, [status, response, parsedHeaders]); - } else { - // remove future object from cache - cache.remove(cfg.url); - } - } - fireCallbacks(response, status); - // TODO(i): we can't null the rawRequest because we might need to be able to call - // rawRequest.getAllResponseHeaders from a promise - // rawRequest = null; - } + /** + * @ngdoc method + * @name angular.module.ng.$http#delete + * @methodOf angular.module.ng.$http + * + * @description + * Shortcut method to perform `DELETE` request + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ /** - * Fire all registered callbacks for given status code + * @ngdoc method + * @name angular.module.ng.$http#head + * @methodOf angular.module.ng.$http * - * This method when: - * - serving response from real request - * - serving response from cache + * @description + * Shortcut method to perform `HEAD` request * - * It does: - * - transform the response - * - call proper callbacks - * - log errors - * - apply the $scope - * - clear parsed headers + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {XhrFuture} Future object */ - function fireCallbacks(response, status) { - var strStatus = status + ''; - - // transform the response - response = transform(response, cfg.transformResponse || $config.transformResponse, rawRequest); - - var idx; // remove from pending requests - if ((idx = indexOf($http.pendingRequests, cfg)) !== -1) - $http.pendingRequests.splice(idx, 1); - - // normalize internal statuses to 0 - status = Math.max(status, 0); - forEach(callbacks, function(callback) { - if (callback.regexp.test(strStatus)) { - try { - // use local var to call it without context - var fn = callback.fn; - fn(response, status, headers); - } catch(e) { - $exceptionHandler(e); - } - } - }); - $rootScope.$apply(); - parsedHeaders = null; - } + /** + * @ngdoc method + * @name angular.module.ng.$http#patch + * @methodOf angular.module.ng.$http + * + * @description + * Shortcut method to perform `PATCH` request + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ /** - * This is the third argument in any user callback - * @see parseHeaders + * @ngdoc method + * @name angular.module.ng.$http#jsonp + * @methodOf angular.module.ng.$http * - * Return single header value or all headers parsed as object. - * Headers all lazy parsed when first requested. + * @description + * Shortcut method to perform `JSONP` request * - * @param {string=} name Name of header - * @returns {string|Object} + * @param {string} url Relative or absolute URL specifying the destination of the request. + * Should contain `JSON_CALLBACK` string. + * @param {Object=} config Optional configuration object + * @returns {XhrFuture} Future object */ - function headers(name) { - if (name) { - return parsedHeaders ? - parsedHeaders[lowercase(name)] || null : - rawRequest.getResponseHeader(name); - } + createShortMethods('get', 'delete', 'head', 'patch', 'jsonp'); - parsedHeaders = parsedHeaders || parseHeaders(rawRequest.getAllResponseHeaders()); + /** + * @ngdoc method + * @name angular.module.ng.$http#post + * @methodOf angular.module.ng.$http + * + * @description + * Shortcut method to perform `POST` request + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ - return parsedHeaders; + /** + * @ngdoc method + * @name angular.module.ng.$http#put + * @methodOf angular.module.ng.$http + * + * @description + * Shortcut method to perform `PUT` request + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {XhrFuture} Future object + */ + createShortMethodsWithData('post', 'put'); + + + return $http; + + + function createShortMethods(names) { + forEach(arguments, function(name) { + $http[name] = function(url, config) { + return $http(extend(config || {}, { + method: name, + url: url + })); + }; + }); } + + function createShortMethodsWithData(name) { + forEach(arguments, function(name) { + $http[name] = function(url, data, config) { + return $http(extend(config || {}, { + method: name, + url: url, + data: data + })); + }; + }); + } + + /** - * Retry the request + * Makes the request * - * @param {Object=} config Optional config object to extend the original configuration - * @returns {HttpPromise} + * !!! ACCESSES CLOSURE VARS: + * $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests */ - this.retry = function(config) { - if (rawRequest) throw 'Can not retry request. Abort pending request first.'; + function sendReq(config, reqData, reqHeaders) { + var deferred = $q.defer(), + promise = deferred.promise, + cache, + cachedResp; - extend(cfg, config); - cfg.method = uppercase(cfg.method); + $http.pendingRequests.push(config); + promise.then(removePendingReq, removePendingReq); - var data = transform(cfg.data, cfg.transformRequest || $config.transformRequest), - headers = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']}, - defHeaders.common, defHeaders[lowercase(cfg.method)], cfg.headers); - var cache = isObject(cfg.cache) && cfg.cache || defaultCache, - fromCache; + if (config.cache && config.method == 'GET') { + cache = isObject(config.cache) ? config.cache : defaultCache; + } - if (cfg.cache && cfg.method == 'GET') { - fromCache = cache.get(cfg.url); - if (fromCache) { - if (fromCache instanceof XhrFuture) { - // cached request has already been sent, but there is no reponse yet, + if (cache) { + cachedResp = cache.get(config.url); + if (cachedResp) { + if (cachedResp.then) { + // cached request has already been sent, but there is no response yet, // we need to register callback and fire callbacks when the request is back // note, we have to get the values from cache and perform transformations on them, // as the configurations don't have to be same - fromCache.on('always', function() { - var requestFromCache = cache.get(cfg.url); - parsedHeaders = requestFromCache[2]; - fireCallbacks(requestFromCache[1], requestFromCache[0]); - }); + cachedResp.then(removePendingReq, removePendingReq); + return cachedResp; } else { - // serving from cache - still needs to be async - $browser.defer(function() { - parsedHeaders = fromCache[2]; - fireCallbacks(fromCache[1], fromCache[0]); - }); + // serving from cache + if (isArray(cachedResp)) { + resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2])); + } else { + resolvePromise(cachedResp, 200, {}); + } } } else { - // put future object into cache - cache.put(cfg.url, self); + // put the promise for the non-transformed response into cache as a placeholder + cache.put(config.url, promise); } } - // really send the request - if (!cfg.cache || cfg.method !== 'GET' || !fromCache) { - rawRequest = $httpBackend(cfg.method, cfg.url, data, done, headers, cfg.timeout); + // if we won't have the response in cache, send the request to the backend + if (!cachedResp) { + $httpBackend(config.method, config.url, reqData, done, reqHeaders, config.timeout); } - $http.pendingRequests.push(cfg); - return self; - }; + return promise; - // just alias so that in stack trace we can see send() instead of retry() - this.send = this.retry; - /** - * Abort the request - */ - this.abort = function() { - if (rawRequest) { - rawRequest.abort(); + /** + * Callback registered to $httpBackend(): + * - caches the response if desired + * - resolves the raw $http promise + * - calls $apply + */ + function done(status, response, headersString) { + if (cache) { + if (isSuccess(status)) { + cache.put(config.url, [status, response, parseHeaders(headersString)]); + } else { + // remove promise from the cache + cache.remove(config.url); + } + } + + resolvePromise(response, status, headersString); + $rootScope.$apply(); } - return this; - }; - /** - * Register a callback function based on status code - * Note: all matched callbacks will be called, preserving registered order ! - * - * Internal statuses: - * `-2` = jsonp error - * `-1` = timeout - * `0` = aborted - * - * @example - * .on('2xx', function(){}); - * .on('2x1', function(){}); - * .on('404', function(){}); - * .on('20x,3xx', function(){}); - * .on('success', function(){}); - * .on('error', function(){}); - * .on('always', function(){}); - * .on('timeout', function(){}); - * .on('abort', function(){}); - * - * @param {string} pattern Status code pattern with "x" for any number - * @param {function(*, number, function)} callback Function to be called when response arrives - * @returns {XhrFuture} - */ - this.on = function(pattern, callback) { - var alias = { - success: '2xx', - error: '-2,-1,0,4xx,5xx', - always: 'xxx,xx,x', - timeout: '-1', - abort: '0' - }; - callbacks.push({ - fn: callback, - // create regexp from given pattern - regexp: new RegExp('^(' + (alias[pattern] || pattern).replace(/,/g, '|'). - replace(/x/g, '.') + ')$') - }); + /** + * Resolves the raw $http promise. + */ + function resolvePromise(response, status, headers) { + // normalize internal statuses to 0 + status = Math.max(status, 0); - return this; - }; + (isSuccess(status) ? deferred.resolve : deferred.reject)({ + data: response, + status: status, + headers: headersGetter(headers), + config: config + }); + } - /** - * Configuration object of the request - */ - this.config = cfg; - } -}]; -} + function removePendingReq() { + var idx = indexOf($http.pendingRequests, config); + if (idx !== -1) $http.pendingRequests.splice(idx, 1); + } + } + }]; +} diff --git a/src/service/httpBackend.js b/src/service/httpBackend.js index c64519d9..c3114814 100644 --- a/src/service/httpBackend.js +++ b/src/service/httpBackend.js @@ -14,6 +14,11 @@ var XHR = window.XMLHttpRequest || function() { * @requires $document * * @description + * HTTP backend used by the {@link angular.module.ng.$http service} that delegates to + * XMLHttpRequest object. + * + * During testing this implementation is swapped with {@link angular.module.ngMock.$httpBackend mock + * $httpBackend} which can be trained with responses. */ function $HttpBackendProvider() { this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { @@ -46,24 +51,21 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, body, locati var xhr = new XHR(); xhr.open(method, url, true); forEach(headers, function(value, key) { - if (value) xhr.setRequestHeader(key, value); + 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); - } - }; - } + // In IE6 and 7, this might be called synchronously when xhr.send below is called and the + // response is in the cache + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + completeRequest( + callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders()); + } + }; + + xhr.send(post || ''); if (timeout > 0) { $browserDefer(function() { @@ -71,23 +73,21 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, body, locati xhr.abort(); }, timeout); } - - return xhr; } - function completeRequest(callback, status, response) { + + function completeRequest(callback, status, response, headersString) { // URL_MATCH is defined in src/service/location.js var protocol = (url.match(URL_MATCH) || ['', locationProtocol])[1]; // fix status code for file protocol (it's always 0) - status = protocol == 'file' ? (response ? 200 : 404) : status; + status = (protocol == 'file') ? (response ? 200 : 404) : status; // normalize IE bug (http://bugs.jquery.com/ticket/1450) status = status == 1223 ? 204 : status; - callback(status, response); + callback(status, response, headersString); $browser.$$completeOutstandingRequest(noop); } }; } - diff --git a/test/angular-mocksSpec.js b/test/angular-mocksSpec.js index a8328f41..88e6a590 100644 --- a/test/angular-mocksSpec.js +++ b/test/angular-mocksSpec.js @@ -465,39 +465,6 @@ describe('mocks', function() { }); - it('should expose given headers', function() { - hb.when('GET', '/u1').respond(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 normalize when header name case when accessed via getResponseHeader', function() { - hb.when('GET', '/u1').respond(200, null, {'X-Fake': 'Header', - 'Content-Type': 'application/json', - 'Location': '/foo'}); - var xhr = hb('GET', '/u1', null, noop, {}); - hb.flush(); - expect(xhr.getResponseHeader('x-fAKE')).toBe('Header'); - expect(xhr.getResponseHeader('content-type')).toBe('application/json'); - expect(xhr.getResponseHeader('Location')).toBe('/foo'); - }); - - - it('should normalize expect header name case when accessed via getResponseHeader', function() { - hb.expect('GET', '/u1').respond(200, null, {'X-Fake': 'Header', - 'Content-Type': 'application/json', - 'Location': '/foo'}); - var xhr = hb('GET', '/u1', null, noop, {}); - hb.flush(); - expect(xhr.getResponseHeader('x-fAKE')).toBe('Header'); - expect(xhr.getResponseHeader('content-type')).toBe('application/json'); - expect(xhr.getResponseHeader('Location')).toBe('/foo'); - }); - - it('should preserve the order of requests', function() { hb.when('GET', '/url1').respond(200, 'first'); hb.when('GET', '/url2').respond(201, 'second'); @@ -508,186 +475,179 @@ describe('mocks', function() { hb.flush(); expect(callback.callCount).toBe(2); - expect(callback.argsForCall[0]).toEqual([201, 'second']); - expect(callback.argsForCall[1]).toEqual([200, 'first']); + expect(callback.argsForCall[0]).toEqual([201, 'second', '']); + expect(callback.argsForCall[1]).toEqual([200, 'first', '']); }); - it('respond() should take function', function() { - hb.when('GET', '/some').respond(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(); + describe('respond()', function() { + it('should take values', function() { + hb.expect('GET', '/url1').respond(200, 'first', {'header': 'val'}); + hb('GET', '/url1', undefined, callback); + 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'); - }); + expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val'); + }); + it('should take function', function() { + hb.expect('GET', '/some').respond(function(m, u, d, h) { + return [301, m + u + ';' + d + ';a=' + h.a, {'Connection': 'keep-alive'}]; + }); - it('expect() should require specified order', function() { - hb.expect('GET', '/url1').respond(200, ''); - hb.expect('GET', '/url2').respond(200, ''); + hb('GET', '/some', 'data', callback, {a: 'b'}); + hb.flush(); - expect(function() { - hb('GET', '/url2', null, noop, {}); - }).toThrow('Unexpected request: GET /url2\nExpected GET /url1'); - }); + expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some;data;a=b', 'Connection: keep-alive'); + }); + it('should default status code to 200', function() { + callback.andCallFake(function(status, response) { + expect(status).toBe(200); + expect(response).toBe('some-data'); + }); - it('expect() should have precendence over when()', function() { - callback.andCallFake(function(status, response) { - expect(status).toBe(300); - expect(response).toBe('expect'); + 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); }); - hb.when('GET', '/url').respond(200, 'when'); - hb.expect('GET', '/url').respond(300, 'expect'); - hb('GET', '/url', null, callback, {}); - hb.flush(); - expect(callback).toHaveBeenCalledOnce(); - }); + it('should default response headers to ""', function() { + hb.expect('GET', '/url1').respond(200, 'first'); + hb.expect('GET', '/url2').respond('second'); + hb('GET', '/url1', null, callback); + hb('GET', '/url2', null, callback); - it ('should throw exception when only headers differes from expectation', function() { - hb.when('GET').respond(200, '', {}); - hb.expect('GET', '/match', undefined, {'Content-Type': 'application/json'}); + hb.flush(); - expect(function() { - hb('GET', '/match', null, noop, {}); - }).toThrow('Expected GET /match with different headers\n' + - 'EXPECTED: {"Content-Type":"application/json"}\nGOT: {}'); + expect(callback.callCount).toBe(2); + expect(callback.argsForCall[0]).toEqual([200, 'first', '']); + expect(callback.argsForCall[1]).toEqual([200, 'second', '']); + }); }); - it ('should throw exception when only data differes from expectation', function() { - hb.when('GET').respond(200, '', {}); - hb.expect('GET', '/match', 'some-data'); - - expect(function() { - hb('GET', '/match', 'different', noop, {}); - }).toThrow('Expected GET /match with different data\n' + - 'EXPECTED: some-data\nGOT: different'); - }); - + describe('expect()', function() { + it('should require specified order', function() { + hb.expect('GET', '/url1').respond(200, ''); + hb.expect('GET', '/url2').respond(200, ''); - it('expect() should without respond() and use respond()', function() { - callback.andCallFake(function(status, response) { - expect(status).toBe(201); - expect(response).toBe('data'); + expect(function() { + hb('GET', '/url2', null, noop, {}); + }).toThrow('Unexpected request: GET /url2\nExpected GET /url1'); }); - hb.when('GET', '/some').respond(201, 'data'); - hb.expect('GET', '/some'); - hb('GET', '/some', null, callback); - hb.flush(); - expect(callback).toHaveBeenCalled(); - expect(function() { hb.verifyNoOutstandingExpectation(); }).not.toThrow(); - }); + it('should have precedence over when()', function() { + callback.andCallFake(function(status, response) { + expect(status).toBe(300); + expect(response).toBe('expect'); + }); + hb.when('GET', '/url').respond(200, 'when'); + hb.expect('GET', '/url').respond(300, 'expect'); - it('flush() should flush requests fired during callbacks', function() { - hb.when('GET').respond(200, ''); - hb('GET', '/some', null, function() { - hb('GET', '/other', null, callback); + hb('GET', '/url', null, callback, {}); + hb.flush(); + expect(callback).toHaveBeenCalledOnce(); }); - hb.flush(); - expect(callback).toHaveBeenCalled(); - }); - - it('flush() should flush given number of pending requests', function() { - hb.when('GET').respond(200, ''); - hb('GET', '/some', null, callback); - hb('GET', '/some', null, callback); - hb('GET', '/some', null, callback); + it ('should throw exception when only headers differs from expectation', function() { + hb.when('GET').respond(200, '', {}); + hb.expect('GET', '/match', undefined, {'Content-Type': 'application/json'}); - hb.flush(2); - expect(callback).toHaveBeenCalled(); - expect(callback.callCount).toBe(2); - }); + expect(function() { + hb('GET', '/match', null, noop, {}); + }).toThrow('Expected GET /match with different headers\n' + + 'EXPECTED: {"Content-Type":"application/json"}\nGOT: {}'); + }); - it('flush() should throw exception when flushing more requests than pending', function() { - hb.when('GET').respond(200, ''); - hb('GET', '/url', null, callback); + it ('should throw exception when only data differs from expectation', function() { + hb.when('GET').respond(200, '', {}); + hb.expect('GET', '/match', 'some-data'); - expect(function() {hb.flush(2);}).toThrow('No more pending request to flush !'); - expect(callback).toHaveBeenCalledOnce(); - }); + expect(function() { + hb('GET', '/match', 'different', noop, {}); + }).toThrow('Expected GET /match with different data\n' + + 'EXPECTED: some-data\nGOT: different'); + }); - it('(flush) should throw exception when no request to flush', function() { - expect(function() {hb.flush();}).toThrow('No pending request to flush !'); + it("should use when's respond() when no expect() respond is defined", function() { + callback.andCallFake(function(status, response) { + expect(status).toBe(201); + expect(response).toBe('data'); + }); - hb.when('GET').respond(200, ''); - hb('GET', '/some', null, callback); - hb.flush(); + hb.when('GET', '/some').respond(201, 'data'); + hb.expect('GET', '/some'); + hb('GET', '/some', null, callback); + hb.flush(); - expect(function() {hb.flush();}).toThrow('No pending request to flush !'); + expect(callback).toHaveBeenCalled(); + expect(function() { hb.verifyNoOutstandingExpectation(); }).not.toThrow(); + }); }); - it('(flush) should throw exception if not all expectations satasfied', function() { - hb.expect('GET', '/url1').respond(); - hb.expect('GET', '/url2').respond(); + describe('flush()', function() { + it('flush() should flush requests fired during callbacks', function() { + hb.when('GET').respond(200, ''); + hb('GET', '/some', null, function() { + hb('GET', '/other', null, callback); + }); - hb('GET', '/url1', null, angular.noop); - expect(function() {hb.flush();}).toThrow('Unsatisfied requests: GET /url2'); - }); + hb.flush(); + expect(callback).toHaveBeenCalled(); + }); - 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'); + it('should flush given number of pending requests', function() { + hb.when('GET').respond(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); }); - 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('should throw exception when flushing more requests than pending', function() { + hb.when('GET').respond(200, ''); + hb('GET', '/url', null, callback); - 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'); + expect(function() {hb.flush(2);}).toThrow('No more pending request to flush !'); + expect(callback).toHaveBeenCalledOnce(); }); - hb.when('GET', '/url1').respond('some-data'); - hb.when('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('should throw exception when no request to flush', function() { + expect(function() {hb.flush();}).toThrow('No pending request to flush !'); - 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').respond(200, ''); + hb('GET', '/some', null, callback); + hb.flush(); + + expect(function() {hb.flush();}).toThrow('No pending request to flush !'); }); - hb.when('GET').respond(201, 'def-response'); - hb.expect('GET', '/some-url'); - hb('GET', '/some-url', null, callback); - hb.flush(); - expect(callback).toHaveBeenCalledOnce(); - hb.verifyNoOutstandingExpectation(); + it('should throw exception if not all expectations satisfied', function() { + hb.expect('GET', '/url1').respond(); + hb.expect('GET', '/url2').respond(); + + hb('GET', '/url1', null, angular.noop); + expect(function() {hb.flush();}).toThrow('Unsatisfied requests: GET /url2'); + }); }); @@ -699,7 +659,7 @@ describe('mocks', function() { }); - it('should throw an exception if no response for expection and no definition', function() { + it('should throw an exception if no response for exception and no definition', function() { hb.expect('GET', '/url'); expect(function() { hb('GET', '/url', null, callback); @@ -762,7 +722,7 @@ describe('mocks', function() { }); - describe('reset', function() { + describe('resetExpectations', function() { it('should remove all expectations', function() { hb.expect('GET', '/u2').respond(200, '', {}); @@ -773,7 +733,7 @@ describe('mocks', function() { }); - it('should remove all responses', function() { + it('should remove all pending responses', function() { var cancelledClb = jasmine.createSpy('cancelled'); hb.expect('GET', '/url').respond(200, ''); diff --git a/test/service/httpBackendSpec.js b/test/service/httpBackendSpec.js index b9bf2b18..1c6d3a51 100644 --- a/test/service/httpBackendSpec.js +++ b/test/service/httpBackendSpec.js @@ -58,18 +58,13 @@ describe('$httpBackend', function() { $backend('POST', 'URL', null, noop, {'X-header1': 'value1', 'X-header2': 'value2'}); xhr = MockXhr.$$lastInstance; - expect(xhr.$$headers).toEqual({ + expect(xhr.$$reqHeaders).toEqual({ 'X-header1': 'value1', 'X-header2': 'value2' }); }); - it('should return raw xhr object', function() { - expect($backend('GET', '/url', null, noop)).toBe(MockXhr.$$lastInstance); - }); - - it('should abort request on timeout', function() { callback.andCallFake(function(status, response) { expect(status).toBe(-1); @@ -91,16 +86,20 @@ describe('$httpBackend', function() { }); - it('should be async even if xhr.send() is sync', function() { - // IE6, IE7 is sync when serving from cache + it('should register onreadystatechange callback before sending', function() { + // send() in IE6, IE7 is sync when serving from cache function SyncXhr() { xhr = this; this.open = this.setRequestHeader = noop; + this.send = function() { this.status = 200; this.responseText = 'response'; this.readyState = 4; + this.onreadystatechange(); }; + + this.getAllResponseHeaders = valueFn(''); } callback.andCallFake(function(status, response) { @@ -108,14 +107,8 @@ describe('$httpBackend', function() { expect(response).toBe('response'); }); - $backend = createHttpBackend($browser, SyncXhr, fakeTimeout); + $backend = createHttpBackend($browser, SyncXhr); $backend('GET', '/url', null, callback); - expect(callback).not.toHaveBeenCalled(); - - fakeTimeout.flush(); - expect(callback).toHaveBeenCalledOnce(); - - (xhr.onreadystatechange || noop)(); expect(callback).toHaveBeenCalledOnce(); }); diff --git a/test/service/httpSpec.js b/test/service/httpSpec.js index 5b8f43f5..c1f8645e 100644 --- a/test/service/httpSpec.js +++ b/test/service/httpSpec.js @@ -66,7 +66,7 @@ describe('$http', function() { expect(data).toBe('Hello!?'); expect(status).toBe(209); callback(); - }) + }); $httpBackend.flush(); expect(callback).toHaveBeenCalledOnce(); })); @@ -550,7 +550,7 @@ describe('$http', function() { }); - describe('transform', function() { + describe('transformData', function() { describe('request', function() { @@ -648,17 +648,19 @@ describe('$http', function() { cache = $cacheFactory('testCache'); })); + function doFirstCacheRequest(method, respStatus, headers) { $httpBackend.expect(method || 'GET', '/url').respond(respStatus || 200, 'content', headers); $http({method: method || 'GET', url: '/url', cache: cache}); $httpBackend.flush(); } - it('should cache GET request when cache is provided', inject(function($browser) { + + it('should cache GET request when cache is provided', inject(function($rootScope) { doFirstCacheRequest(); $http({method: 'get', url: '/url', cache: cache}).success(callback); - $browser.defer.flush(); + $rootScope.$digest(); expect(callback).toHaveBeenCalledOnce(); expect(callback.mostRecentCall.args[0]).toBe('content'); @@ -737,7 +739,7 @@ describe('$http', function() { }); - it('should cache the headers as well', inject(function($browser) { + it('should cache the headers as well', inject(function($rootScope) { doFirstCacheRequest('GET', 200, {'content-encoding': 'gzip', 'server': 'Apache'}); callback.andCallFake(function(r, s, headers) { expect(headers()).toEqual({'content-encoding': 'gzip', 'server': 'Apache'}); @@ -745,24 +747,37 @@ describe('$http', function() { }); $http({method: 'GET', url: '/url', cache: cache}).success(callback); - $browser.defer.flush(); + $rootScope.$digest(); expect(callback).toHaveBeenCalledOnce(); })); - it('should cache status code as well', inject(function($browser) { + it('should not share the cached headers object instance', inject(function($rootScope) { + doFirstCacheRequest('GET', 200, {'content-encoding': 'gzip', 'server': 'Apache'}); + callback.andCallFake(function(r, s, headers) { + expect(headers()).toEqual(cache.get('/url')[2]); + expect(headers()).not.toBe(cache.get('/url')[2]); + }); + + $http({method: 'GET', url: '/url', cache: cache}).success(callback); + $rootScope.$digest(); + expect(callback).toHaveBeenCalledOnce(); + })); + + + it('should cache status code as well', inject(function($rootScope) { doFirstCacheRequest('GET', 201); callback.andCallFake(function(r, status, h) { expect(status).toBe(201); }); $http({method: 'get', url: '/url', cache: cache}).success(callback); - $browser.defer.flush(); + $rootScope.$digest(); expect(callback).toHaveBeenCalledOnce(); })); - it('should use cache even if request fired before first response is back', function() { + it('should use cache even if second request was made before the first returned', function() { $httpBackend.expect('GET', '/url').respond(201, 'fake-response'); callback.andCallFake(function(response, status, headers) { @@ -777,6 +792,22 @@ describe('$http', function() { expect(callback).toHaveBeenCalled(); expect(callback.callCount).toBe(2); }); + + + it('should default to status code 200 and empty headers if cache contains a non-array element', + inject(function($rootScope) { + cache.put('/myurl', 'simple response'); + $http.get('/myurl', {cache: cache}).success(function(data, status, headers) { + expect(data).toBe('simple response'); + expect(status).toBe(200); + expect(headers()).toEqual({}); + callback(); + }); + + $rootScope.$digest(); + expect(callback).toHaveBeenCalledOnce(); + }) + ); }); @@ -794,7 +825,7 @@ describe('$http', function() { }); - it('should update pending requests even when served from cache', inject(function($browser) { + it('should update pending requests even when served from cache', inject(function($rootScope) { $httpBackend.when('GET').respond(200); $http({method: 'get', url: '/cached', cache: true}); @@ -807,7 +838,7 @@ describe('$http', function() { $http({method: 'get', url: '/cached', cache: true}); expect($http.pendingRequests.length).toBe(1); - $browser.defer.flush(); + $rootScope.$apply(); expect($http.pendingRequests.length).toBe(0); })); diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js index e8ff4b27..09d807b5 100644 --- a/test/widgetsSpec.js +++ b/test/widgetsSpec.js @@ -72,7 +72,6 @@ describe('widget', function() { $rootScope.childScope.name = 'misko'; $rootScope.url = 'myUrl'; $rootScope.$digest(); - $browser.defer.flush(); expect(element.text()).toEqual('misko'); })); @@ -86,7 +85,6 @@ describe('widget', function() { $rootScope.childScope.name = 'igor'; $rootScope.url = 'myUrl'; $rootScope.$digest(); - $browser.defer.flush(); expect(element.text()).toEqual('igor'); @@ -103,7 +101,6 @@ describe('widget', function() { element = $compile(element)($rootScope); $rootScope.url = 'myUrl'; $rootScope.$digest(); - $browser.defer.flush(); // TODO(misko): because we are using scope==this, the eval gets registered // during the flush phase and hence does not get called. @@ -125,7 +122,6 @@ describe('widget', function() { $rootScope.url = 'myUrl'; $rootScope.$digest(); - $browser.defer.flush(); expect(element.text()).toEqual('my partial'); expect($rootScope.loaded).toBe(true); @@ -141,7 +137,6 @@ describe('widget', function() { $rootScope.url = 'myUrl'; $rootScope.$digest(); - $browser.defer.flush(); expect($rootScope.$$childHead).toBeTruthy(); $rootScope.url = null; @@ -166,7 +161,6 @@ describe('widget', function() { $rootScope.url = 'myUrl'; $rootScope.$digest(); - $browser.defer.flush(); expect(element.text()).toEqual('my partial'); dealoc($rootScope); })); @@ -199,7 +193,6 @@ describe('widget', function() { }); $rootScope.$digest(); - $browser.defer.flush(); expect(element.text()).toBe('my partial'); })); @@ -746,7 +739,6 @@ describe('widget', function() { $rootScope.log = []; $location.path('/foo'); $rootScope.$apply(); - $browser.defer.flush(); expect($rootScope.log).toEqual(['parent', 'init', 'child']); })); @@ -801,7 +793,6 @@ describe('widget', function() { }); $rootScope.$digest(); - $browser.defer.flush(); expect(element.text()).toBe('my partial'); })); }); |
