mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-14 01:33:11 +00:00
feat($http): turn mock backend into a decorator + e2e testing support
- provider -> decorator - autoflush + passThrough mode - fix noop -> angular.noop
This commit is contained in:
parent
23f8da7cbb
commit
67338ce061
2 changed files with 173 additions and 122 deletions
250
src/angular-mocks.js
vendored
250
src/angular-mocks.js
vendored
|
|
@ -21,7 +21,7 @@ angular.module.ngMock = function($provide){
|
||||||
$provide.service('$browser', angular.module.ngMock.$BrowserProvider);
|
$provide.service('$browser', angular.module.ngMock.$BrowserProvider);
|
||||||
$provide.service('$exceptionHandler', angular.module.ngMock.$ExceptionHandlerProvider);
|
$provide.service('$exceptionHandler', angular.module.ngMock.$ExceptionHandlerProvider);
|
||||||
$provide.service('$log', angular.module.ngMock.$LogProvider);
|
$provide.service('$log', angular.module.ngMock.$LogProvider);
|
||||||
$provide.service('$httpBackend', angular.module.ngMock.$HttpBackendProvider);
|
$provide.decorator('$httpBackend', angular.module.ngMock.$httpBackendDecorator);
|
||||||
};
|
};
|
||||||
angular.module.ngMock.$inject = ['$provide'];
|
angular.module.ngMock.$inject = ['$provide'];
|
||||||
|
|
||||||
|
|
@ -56,8 +56,8 @@ angular.module.ngMock.$Browser = function() {
|
||||||
self.pollFns = [];
|
self.pollFns = [];
|
||||||
|
|
||||||
// TODO(vojta): remove this temporary api
|
// TODO(vojta): remove this temporary api
|
||||||
self.$$completeOutstandingRequest = noop;
|
self.$$completeOutstandingRequest = angular.noop;
|
||||||
self.$$incOutstandingRequestCount = noop;
|
self.$$incOutstandingRequestCount = angular.noop;
|
||||||
|
|
||||||
|
|
||||||
// register url polling fn
|
// register url polling fn
|
||||||
|
|
@ -593,144 +593,160 @@ angular.module.ngMock.dump = function(object){
|
||||||
* respond with static or dynamic responses via the `expect` and `when` apis and their shortcuts
|
* respond with static or dynamic responses via the `expect` and `when` apis and their shortcuts
|
||||||
* (`expectGET`, `whenPOST`, etc).
|
* (`expectGET`, `whenPOST`, etc).
|
||||||
*/
|
*/
|
||||||
angular.module.ngMock.$HttpBackendProvider = function() {
|
angular.module.ngMock.$httpBackendDecorator = function($delegate, $defer) {
|
||||||
this.$get = function() {
|
var definitions = [],
|
||||||
var definitions = [],
|
expectations = [],
|
||||||
expectations = [],
|
responses = [],
|
||||||
responses = [];
|
responsesPush = angular.bind(responses, responses.push),
|
||||||
|
autoflush = false;
|
||||||
|
|
||||||
function createResponse(status, data, headers) {
|
function createResponse(status, data, headers) {
|
||||||
if (isFunction(status)) return status;
|
if (angular.isFunction(status)) return status;
|
||||||
|
|
||||||
return function() {
|
return function() {
|
||||||
return angular.isNumber(status)
|
return angular.isNumber(status)
|
||||||
? [status, data, headers]
|
? [status, data, headers]
|
||||||
: [200, status, data];
|
: [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;
|
||||||
|
|
||||||
|
function prettyPrint(data) {
|
||||||
|
return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
|
||||||
|
? data
|
||||||
|
: angular.toJson(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(vojta): change params to: method, url, data, headers, callback
|
if (expectation && expectation.match(method, url)) {
|
||||||
function $httpBackend(method, url, data, callback, headers) {
|
if (!expectation.matchData(data))
|
||||||
var xhr = new MockXhr(),
|
throw Error('Expected ' + expectation + ' with different data\n' +
|
||||||
expectation = expectations[0],
|
'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
|
||||||
wasExpected = false;
|
|
||||||
|
|
||||||
function prettyPrint(data) {
|
if (!expectation.matchHeaders(headers))
|
||||||
return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
|
throw Error('Expected ' + expectation + ' with different headers\n' +
|
||||||
? data
|
'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + prettyPrint(headers));
|
||||||
: angular.toJson(data);
|
|
||||||
|
expectations.shift();
|
||||||
|
|
||||||
|
if (expectation.response) {
|
||||||
|
responses.push(function() {
|
||||||
|
var response = expectation.response(method, url, data, headers);
|
||||||
|
xhr.$$respHeaders = response[2];
|
||||||
|
callback(response[0], response[1], xhr.getAllResponseHeaders());
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
wasExpected = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (expectation && expectation.match(method, url)) {
|
var i = -1, definition;
|
||||||
if (!expectation.matchData(data))
|
while ((definition = definitions[++i])) {
|
||||||
throw Error('Expected ' + expectation + ' with different data\n' +
|
if (definition.match(method, url, data, headers || {})) {
|
||||||
'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
|
if (definition.response) {
|
||||||
|
(autoflush ? $defer : responsesPush)(function() {
|
||||||
if (!expectation.matchHeaders(headers))
|
|
||||||
throw Error('Expected ' + expectation + ' with different headers\n' +
|
|
||||||
'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + prettyPrint(headers));
|
|
||||||
|
|
||||||
expectations.shift();
|
|
||||||
|
|
||||||
if (expectation.response) {
|
|
||||||
responses.push(function() {
|
|
||||||
var response = expectation.response(method, url, data, headers);
|
|
||||||
xhr.$$respHeaders = response[2];
|
|
||||||
callback(response[0], response[1], xhr.getAllResponseHeaders());
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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 = definition.response(method, url, data, headers);
|
var response = definition.response(method, url, data, headers);
|
||||||
xhr.$$respHeaders = response[2];
|
xhr.$$respHeaders = response[2];
|
||||||
callback(response[0], response[1], xhr.getAllResponseHeaders());
|
callback(response[0], response[1], xhr.getAllResponseHeaders());
|
||||||
});
|
});
|
||||||
return;
|
} else if (definition.passThrough) {
|
||||||
}
|
$delegate(method, url, data, callback, headers);
|
||||||
|
} else throw Error('No response defined !');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
throw wasExpected ?
|
|
||||||
Error('No response defined !') :
|
|
||||||
Error('Unexpected request: ' + method + ' ' + url + '\n' +
|
|
||||||
(expectation ? 'Expected ' + expectation : 'No more request expected'));
|
|
||||||
}
|
}
|
||||||
|
throw wasExpected ?
|
||||||
|
Error('No response defined !') :
|
||||||
|
Error('Unexpected request: ' + method + ' ' + url + '\n' +
|
||||||
|
(expectation ? 'Expected ' + expectation : 'No more request expected'));
|
||||||
|
}
|
||||||
|
|
||||||
$httpBackend.when = function(method, url, data, headers) {
|
$httpBackend.when = function(method, url, data, headers) {
|
||||||
var definition = new MockHttpExpectation(method, url, data, headers);
|
var definition = new MockHttpExpectation(method, url, data, headers);
|
||||||
definitions.push(definition);
|
definitions.push(definition);
|
||||||
return {
|
return {
|
||||||
respond: function(status, data, headers) {
|
respond: function(status, data, headers) {
|
||||||
definition.response = createResponse(status, data, headers);
|
definition.response = createResponse(status, data, headers);
|
||||||
}
|
},
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
createShortMethods('when');
|
passThrough: function() {
|
||||||
|
definition.passThrough = true;
|
||||||
|
|
||||||
$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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
createShortMethods('expect');
|
|
||||||
|
|
||||||
|
|
||||||
$httpBackend.flush = function(count) {
|
|
||||||
if (!responses.length) throw Error('No pending request to flush !');
|
|
||||||
|
|
||||||
if (angular.isDefined(count)) {
|
|
||||||
while (count--) {
|
|
||||||
if (!responses.length) throw Error('No more pending request to flush !');
|
|
||||||
responses.shift()();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (responses.length) {
|
|
||||||
responses.shift()();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$httpBackend.verifyNoOutstandingExpectation();
|
|
||||||
};
|
|
||||||
|
|
||||||
$httpBackend.verifyNoOutstandingExpectation = function() {
|
|
||||||
if (expectations.length) {
|
|
||||||
throw Error('Unsatisfied requests: ' + expectations.join(', '));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
$httpBackend.verifyNoOutstandingRequest = function() {
|
createShortMethods('when');
|
||||||
if (responses.length) {
|
|
||||||
throw Error('Unflushed requests: ' + responses.length);
|
|
||||||
|
$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.resetExpectations = function() {
|
createShortMethods('expect');
|
||||||
expectations = [];
|
|
||||||
responses = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
return $httpBackend;
|
|
||||||
|
|
||||||
|
|
||||||
function createShortMethods(prefix) {
|
$httpBackend.flush = function(count) {
|
||||||
angular.forEach(['GET', 'PUT', 'POST', 'DELETE', 'PATCH', 'JSONP'], function(method) {
|
if (!responses.length) throw Error('No pending request to flush !');
|
||||||
$httpBackend[prefix + method] = function(url, data, headers) {
|
|
||||||
return $httpBackend[prefix](method, url, data, headers)
|
if (angular.isDefined(count)) {
|
||||||
}
|
while (count--) {
|
||||||
});
|
if (!responses.length) throw Error('No more pending request to flush !');
|
||||||
|
responses.shift()();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (responses.length) {
|
||||||
|
responses.shift()();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$httpBackend.verifyNoOutstandingExpectation();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$httpBackend.autoflush = function(val) {
|
||||||
|
if (arguments.length) {
|
||||||
|
autoflush = !!val;
|
||||||
|
} else {
|
||||||
|
return autoflush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$httpBackend.verifyNoOutstandingExpectation = function() {
|
||||||
|
if (expectations.length) {
|
||||||
|
throw Error('Unsatisfied requests: ' + expectations.join(', '));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$httpBackend.verifyNoOutstandingRequest = function() {
|
||||||
|
if (responses.length) {
|
||||||
|
throw Error('Unflushed requests: ' + responses.length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$httpBackend.resetExpectations = function() {
|
||||||
|
expectations = [];
|
||||||
|
responses = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
return $httpBackend;
|
||||||
|
|
||||||
|
|
||||||
|
function createShortMethods(prefix) {
|
||||||
|
angular.forEach(['GET', 'PUT', 'POST', 'DELETE', 'PATCH', 'JSONP'], function(method) {
|
||||||
|
$httpBackend[prefix + method] = function(url, data, headers) {
|
||||||
|
return $httpBackend[prefix](method, url, data, headers)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function MockHttpExpectation(method, url, data, headers) {
|
function MockHttpExpectation(method, url, data, headers) {
|
||||||
|
|
@ -816,7 +832,7 @@ function MockXhr() {
|
||||||
return lines.join('\n');
|
return lines.join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.abort = noop;
|
this.abort = angular.noop;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.jstestdriver && (function(window){
|
window.jstestdriver && (function(window){
|
||||||
|
|
|
||||||
45
test/angular-mocksSpec.js
vendored
45
test/angular-mocksSpec.js
vendored
|
|
@ -375,12 +375,19 @@ describe('mocks', function() {
|
||||||
|
|
||||||
|
|
||||||
describe('$httpBackend', function() {
|
describe('$httpBackend', function() {
|
||||||
var hb, callback;
|
var hb, callback, realBackendSpy;
|
||||||
|
|
||||||
beforeEach(inject(function($httpBackend) {
|
beforeEach(inject(
|
||||||
callback = jasmine.createSpy('callback');
|
function($provide) {
|
||||||
hb = $httpBackend;
|
realBackendSpy = jasmine.createSpy('realBackend');
|
||||||
}));
|
$provide.value('$httpBackend', realBackendSpy);
|
||||||
|
$provide.decorator('$httpBackend', angular.module.ngMock.$httpBackendDecorator)
|
||||||
|
},
|
||||||
|
function($httpBackend) {
|
||||||
|
callback = jasmine.createSpy('callback');
|
||||||
|
hb = $httpBackend;
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
|
||||||
it('should respond with first matched definition', function() {
|
it('should respond with first matched definition', function() {
|
||||||
|
|
@ -676,6 +683,34 @@ describe('mocks', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('definitions with passThrough delegation', function() {
|
||||||
|
it('should delegate requests to the real backend when passThrough is invoked', function() {
|
||||||
|
hb.when('GET', /\/passThrough\/.*/).passThrough();
|
||||||
|
|
||||||
|
expect(hb('GET', '/passThrough/23', null, callback));
|
||||||
|
expect(realBackendSpy).
|
||||||
|
toHaveBeenCalledOnceWith('GET', '/passThrough/23', null, callback, undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('autoflush', function() {
|
||||||
|
it('should flush responses via $defer when autoflush is turned on', inject(
|
||||||
|
function($browser) {
|
||||||
|
expect(hb.autoflush()).toBe(false);
|
||||||
|
hb.autoflush(true);
|
||||||
|
expect(hb.autoflush()).toBe(true);
|
||||||
|
|
||||||
|
hb.when('GET', '/foo').respond('bar');
|
||||||
|
hb('GET', '/foo', null, callback);
|
||||||
|
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
$browser.defer.flush();
|
||||||
|
expect(callback).toHaveBeenCalledOnce();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('verifyExpectations', function() {
|
describe('verifyExpectations', function() {
|
||||||
|
|
||||||
it('should throw exception if not all expectations were satisfied', function() {
|
it('should throw exception if not all expectations were satisfied', function() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue