Skip to content

Commit 5ad0c7d

Browse files
vojtajinaIgorMinar
authored andcommitted
feat($httpBackend): extract $browser.xhr into separate service
- remove whole $browser.xhr stuff - remove whole mock $browser.xhr stuff - add $httpBackend service + migrate unit tests from $browser - add temporary API to access $browser's outstandingRequests count
1 parent 540701a commit 5ad0c7d

5 files changed

Lines changed: 288 additions & 456 deletions

File tree

src/angular-mocks.js

Lines changed: 20 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ angular.module.ngMock.$Browser = function() {
5757
self.$$lastUrl = self.$$url; // used by url polling fn
5858
self.pollFns = [];
5959

60+
// TODO(vojta): remove this temporary api
61+
self.$$completeOutstandingRequest = noop;
62+
self.$$incOutstandingRequestCount = noop;
63+
6064

6165
// register url polling fn
6266

@@ -73,165 +77,6 @@ angular.module.ngMock.$Browser = function() {
7377
return listener;
7478
};
7579

76-
77-
/**
78-
* @ngdoc method
79-
* @name angular.module.ngMock.$browser#xhr
80-
* @methodOf angular.module.ngMock.$browser
81-
*
82-
* @description
83-
* Generic method for training browser to expect a request in a test and respond to it.
84-
*
85-
* See also convenience methods for browser training:
86-
*
87-
* - {@link #xhr.expectGET}
88-
* - {@link #xhr.expectPOST}
89-
* - {@link #xhr.expectPUT}
90-
* - {@link #xhr.expectDELETE}
91-
* - {@link #xhr.expectJSON}
92-
*
93-
* To flush pending requests in tests use
94-
* {@link #xhr.flush}.
95-
*
96-
* @param {string} method Expected HTTP method.
97-
* @param {string} url Url path for which a request is expected.
98-
* @param {(object|string)=} data Expected body of the (POST) HTTP request.
99-
* @param {function(number, *)} callback Callback to call when response is flushed.
100-
* @param {object} headers Key-value pairs of expected headers.
101-
* @returns {object} Response configuration object. You can call its `respond()` method to
102-
* configure what should the browser mock return when the response is
103-
* {@link #xhr.flush flushed}.
104-
*/
105-
self.xhr = function(method, url, data, callback, headers) {
106-
headers = headers || {};
107-
if (data && angular.isObject(data)) data = angular.toJson(data);
108-
if (data && angular.isString(data)) url += "|" + data;
109-
var expect = expectations[method] || {};
110-
var expectation = expect[url];
111-
if (!expectation) {
112-
throw new Error("Unexpected request for method '" + method + "' and url '" + url + "'.");
113-
}
114-
requests.push(function() {
115-
angular.forEach(expectation.headers, function(value, key){
116-
if (headers[key] !== value) {
117-
throw new Error("Missing HTTP request header: " + key + ": " + value);
118-
}
119-
});
120-
callback(expectation.code, expectation.response);
121-
});
122-
// TODO(vojta): return mock request object
123-
};
124-
self.xhr.expectations = expectations;
125-
self.xhr.requests = requests;
126-
self.xhr.expect = function(method, url, data, headers) {
127-
if (data && angular.isObject(data)) data = angular.toJson(data);
128-
if (data && angular.isString(data)) url += "|" + data;
129-
var expect = expectations[method] || (expectations[method] = {});
130-
return {
131-
respond: function(code, response) {
132-
if (!angular.isNumber(code)) {
133-
response = code;
134-
code = 200;
135-
}
136-
expect[url] = {code:code, response:response, headers: headers || {}};
137-
}
138-
};
139-
};
140-
141-
/**
142-
* @ngdoc method
143-
* @name angular.module.ngMock.$browser#xhr.expectGET
144-
* @methodOf angular.module.ngMock.$browser
145-
*
146-
* @description
147-
* Trains browser to expect a `GET` request and respond to it.
148-
*
149-
* @param {string} url Url path for which a request is expected.
150-
* @returns {object} Response configuration object. You can call its `respond()` method to
151-
* configure what should the browser mock return when the response is
152-
* {@link angular.module.ngMock.$browser#xhr.flush flushed}.
153-
*/
154-
self.xhr.expectGET = angular.bind(self, self.xhr.expect, 'GET');
155-
156-
/**
157-
* @ngdoc method
158-
* @name angular.module.ngMock.$browser#xhr.expectPOST
159-
* @methodOf angular.module.ngMock.$browser
160-
*
161-
* @description
162-
* Trains browser to expect a `POST` request and respond to it.
163-
*
164-
* @param {string} url Url path for which a request is expected.
165-
* @returns {object} Response configuration object. You can call its `respond()` method to
166-
* configure what should the browser mock return when the response is
167-
* {@link angular.module.ngMock.$browser#xhr.flush flushed}.
168-
*/
169-
self.xhr.expectPOST = angular.bind(self, self.xhr.expect, 'POST');
170-
171-
/**
172-
* @ngdoc method
173-
* @name angular.module.ngMock.$browser#xhr.expectDELETE
174-
* @methodOf angular.module.ngMock.$browser
175-
*
176-
* @description
177-
* Trains browser to expect a `DELETE` request and respond to it.
178-
*
179-
* @param {string} url Url path for which a request is expected.
180-
* @returns {object} Response configuration object. You can call its `respond()` method to
181-
* configure what should the browser mock return when the response is
182-
* {@link angular.module.ngMock.$browser#xhr.flush flushed}.
183-
*/
184-
self.xhr.expectDELETE = angular.bind(self, self.xhr.expect, 'DELETE');
185-
186-
/**
187-
* @ngdoc method
188-
* @name angular.module.ngMock.$browser#xhr.expectPUT
189-
* @methodOf angular.module.ngMock.$browser
190-
*
191-
* @description
192-
* Trains browser to expect a `PUT` request and respond to it.
193-
*
194-
* @param {string} url Url path for which a request is expected.
195-
* @returns {object} Response configuration object. You can call its `respond()` method to
196-
* configure what should the browser mock return when the response is
197-
* {@link angular.module.ngMock.$browser#xhr.flush flushed}.
198-
*/
199-
self.xhr.expectPUT = angular.bind(self, self.xhr.expect, 'PUT');
200-
201-
/**
202-
* @ngdoc method
203-
* @name angular.module.ngMock.$browser#xhr.expectJSON
204-
* @methodOf angular.module.ngMock.$browser
205-
*
206-
* @description
207-
* Trains browser to expect a `JSON` request and respond to it.
208-
*
209-
* @param {string} url Url path for which a request is expected.
210-
* @returns {object} Response configuration object. You can call its `respond()` method to
211-
* configure what should the browser mock return when the response is
212-
* {@link angular.module.ngMock.$browser#xhr.flush flushed}.
213-
*/
214-
self.xhr.expectJSON = angular.bind(self, self.xhr.expect, 'JSON');
215-
216-
/**
217-
* @ngdoc method
218-
* @name angular.module.ngMock.$browser#xhr.flush
219-
* @methodOf angular.module.ngMock.$browser
220-
*
221-
* @description
222-
* Flushes all pending requests and executes xhr callbacks with the trained response as the
223-
* argument.
224-
*/
225-
self.xhr.flush = function() {
226-
if (requests.length == 0) {
227-
throw new Error("No xhr requests to be flushed!");
228-
}
229-
230-
while(requests.length) {
231-
requests.pop()();
232-
}
233-
};
234-
23580
self.cookieHash = {};
23681
self.lastCookieHash = {};
23782
self.deferredFns = [];
@@ -871,9 +716,24 @@ function MockHttpExpectation(method, url, data, headers) {
871716

872717
function MockXhr() {
873718

874-
// hack for testing $http
719+
// hack for testing $http, $httpBackend
875720
MockXhr.$$lastInstance = this;
876721

722+
this.open = function(method, url, async) {
723+
this.$$method = method;
724+
this.$$url = url;
725+
this.$$async = async;
726+
this.$$headers = {};
727+
};
728+
729+
this.send = function(data) {
730+
this.$$data = data;
731+
};
732+
733+
this.setRequestHeader = function(key, value) {
734+
this.$$headers[key] = value;
735+
};
736+
877737
this.getResponseHeader = function(name) {
878738
return this.$$headers[name];
879739
};

src/service/browser.js

Lines changed: 5 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
'use strict';
22

3-
//////////////////////////////
4-
// Browser
5-
//////////////////////////////
6-
var XHR = window.XMLHttpRequest || function() {
7-
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
8-
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
9-
try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
10-
throw new Error("This browser does not support XMLHttpRequest.");
11-
};
12-
13-
143
/**
154
* @ngdoc object
165
* @name angular.module.ng.$browser
@@ -33,7 +22,7 @@ var XHR = window.XMLHttpRequest || function() {
3322
* @param {object} $log console.log or an object with the same interface.
3423
* @param {object} $sniffer $sniffer service
3524
*/
36-
function Browser(window, document, body, XHR, $log, $sniffer) {
25+
function Browser(window, document, body, $log, $sniffer) {
3726
var self = this,
3827
rawDocument = document[0],
3928
location = window.location,
@@ -44,13 +33,12 @@ function Browser(window, document, body, XHR, $log, $sniffer) {
4433

4534
self.isMock = false;
4635

47-
//////////////////////////////////////////////////////////////
48-
// XHR API
49-
//////////////////////////////////////////////////////////////
50-
var idCounter = 0;
5136
var outstandingRequestCount = 0;
5237
var outstandingRequestCallbacks = [];
5338

39+
// TODO(vojta): remove this temporary api
40+
self.$$completeOutstandingRequest = completeOutstandingRequest;
41+
self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5442

5543
/**
5644
* Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
@@ -73,88 +61,6 @@ function Browser(window, document, body, XHR, $log, $sniffer) {
7361
}
7462
}
7563

76-
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
77-
function fixStatus(status) {
78-
return status == 1223 ? 204 : status;
79-
}
80-
81-
/**
82-
* @ngdoc method
83-
* @name angular.module.ng.$browser#xhr
84-
* @methodOf angular.module.ng.$browser
85-
*
86-
* @param {string} method Requested method (get|post|put|delete|head|json)
87-
* @param {string} url Requested url
88-
* @param {?string} post Post data to send (null if nothing to post)
89-
* @param {function(number, string)} callback Function that will be called on response
90-
* @param {object=} header additional HTTP headers to send with XHR.
91-
* Standard headers are:
92-
* <ul>
93-
* <li><tt>Content-Type</tt>: <tt>application/x-www-form-urlencoded</tt></li>
94-
* <li><tt>Accept</tt>: <tt>application/json, text/plain, &#42;/&#42;</tt></li>
95-
* <li><tt>X-Requested-With</tt>: <tt>XMLHttpRequest</tt></li>
96-
* </ul>
97-
*
98-
* @param {number=} timeout Timeout in ms, when the request will be aborted
99-
* @returns {XMLHttpRequest|undefined} Raw XMLHttpRequest object or undefined when JSONP method
100-
*
101-
* @description
102-
* Send ajax request
103-
*
104-
* TODO(vojta): change signature of this method to (method, url, data, headers, callback)
105-
*/
106-
self.xhr = function(method, url, post, callback, headers, timeout) {
107-
outstandingRequestCount ++;
108-
if (lowercase(method) == 'jsonp') {
109-
var callbackId = ("angular_" + Math.random() + '_' + (idCounter++)).replace(/\d\./, '');
110-
window[callbackId] = function(data) {
111-
window[callbackId].data = data;
112-
};
113-
114-
var script = self.addJs(url.replace('JSON_CALLBACK', callbackId), function() {
115-
if (window[callbackId].data) {
116-
completeOutstandingRequest(callback, 200, window[callbackId].data);
117-
} else {
118-
completeOutstandingRequest(callback, -2);
119-
}
120-
delete window[callbackId];
121-
body[0].removeChild(script);
122-
});
123-
} else {
124-
var xhr = new XHR();
125-
xhr.open(method, url, true);
126-
forEach(headers, function(value, key) {
127-
if (value) xhr.setRequestHeader(key, value);
128-
});
129-
130-
var status;
131-
xhr.send(post || '');
132-
133-
// IE6, IE7 bug - does sync when serving from cache
134-
if (xhr.readyState == 4) {
135-
setTimeout(function() {
136-
completeOutstandingRequest(callback, fixStatus(status || xhr.status), xhr.responseText);
137-
}, 0);
138-
} else {
139-
xhr.onreadystatechange = function() {
140-
if (xhr.readyState == 4) {
141-
completeOutstandingRequest(callback, fixStatus(status || xhr.status),
142-
xhr.responseText);
143-
}
144-
};
145-
}
146-
147-
if (timeout > 0) {
148-
setTimeout(function() {
149-
status = -1;
150-
xhr.abort();
151-
}, timeout);
152-
}
153-
154-
return xhr;
155-
}
156-
};
157-
15864
/**
15965
* @private
16066
* Note: this method is used only by scenario runner
@@ -502,6 +408,6 @@ function Browser(window, document, body, XHR, $log, $sniffer) {
502408
function $BrowserProvider(){
503409
this.$get = ['$window', '$log', '$sniffer', '$document',
504410
function( $window, $log, $sniffer, $document){
505-
return new Browser($window, $document, $document.find('body'), XHR, $log, $sniffer);
411+
return new Browser($window, $document, $document.find('body'), $log, $sniffer);
506412
}];
507413
}

0 commit comments

Comments
 (0)