Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

feat(ngMock, ngMockE2E): add option to match latest definition for $h… #16560

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions src/ngMock/angular-mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -1342,6 +1342,7 @@ angular.mock.$httpBackendDecorator =
function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
var definitions = [],
expectations = [],
matchLatestDefinition = false,
responses = [],
responsesPush = angular.bind(responses, responses.push),
copy = angular.copy,
Expand Down Expand Up @@ -1430,8 +1431,9 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
wasExpected = true;
}

var i = -1, definition;
while ((definition = definitions[++i])) {
var i = matchLatestDefinition ? definitions.length : -1, definition;

while ((definition = definitions[matchLatestDefinition ? --i : ++i])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is dangerous stuff due to the subtle difference between prefix and postfix operators. But given that we were doing this already I guess it'll be fine.

if (definition.match(method, url, data, headers || {})) {
if (definition.response) {
// if $browser specified, we do auto flush all requests
Expand Down Expand Up @@ -1507,6 +1509,47 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
return chain;
};

/**
* @ngdoc method
* @name $httpBackend#matchLatestDefinition
* @description
* This method can be used to change which mocked responses `$httpBackend` returns, when defining
* them with {@link ngMock.$httpBackend#when $httpBackend.when()} (and shortcut methods).
* By default, `$httpBackend` returns the first definition that matches. When setting
* `$http.matchLatestDefinition(true)`, it will use the last response that matches, i.e. the
* one that was added last.
*
* ```js
* hb.when('GET', '/url1').respond(200, 'content', {});
* hb.when('GET', '/url1').respond(201, 'another', {});
* hb('GET', '/url1'); // receives "content"
*
* $http.matchLatestDefinition(true)
* hb('GET', '/url1'); // receives "another"
*
* hb.when('GET', '/url1').respond(201, 'onemore', {});
* hb('GET', '/url1'); // receives "onemore"
* ```
*
* This is useful if a you have a default response that is overriden inside specific tests.
*
* Note that different from config methods on providers, `matchLatestDefinition()` can be changed
* even when the application is already running.
*
* @param {Boolean=} value value to set, either `true` or `false`. Default is `false`.
* If omitted, it will return the current value.
* @return {$httpBackend|Boolean} self when used as a setter, and the current value when used
* as a getter
*/
$httpBackend.matchLatestDefinitionEnabled = function(value) {
if (isDefined(value)) {
matchLatestDefinition = value;
return this;
} else {
return matchLatestDefinition;
}
};

/**
* @ngdoc method
* @name $httpBackend#whenGET
Expand Down
74 changes: 73 additions & 1 deletion test/ngMock/angular-mocksSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@ describe('ngMock', function() {
});


it('should respond with first matched definition', function() {
it('should respond with first matched definition by default', function() {
hb.when('GET', '/url1').respond(200, 'content', {});
hb.when('GET', '/url1').respond(201, 'another', {});

Expand All @@ -1106,6 +1106,78 @@ describe('ngMock', function() {
});


describe('matchLatestDefinitionEnabled()', function() {

it('should be set to false by default', function() {
expect(hb.matchLatestDefinitionEnabled()).toBe(false);
});


it('should allow to change the value', function() {
hb.matchLatestDefinitionEnabled(true);
expect(hb.matchLatestDefinitionEnabled()).toBe(true);
});


it('should return the httpBackend when used as a setter', function() {
expect(hb.matchLatestDefinitionEnabled(true)).toBe(hb);
});


it('should respond with the first matched definition when false',
function() {
hb.matchLatestDefinitionEnabled(false);

hb.when('GET', '/url1').respond(200, 'content', {});
hb.when('GET', '/url1').respond(201, 'another', {});

callback.and.callFake(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 respond with latest matched definition when true',
function() {
hb.matchLatestDefinitionEnabled(true);

hb.when('GET', '/url1').respond(200, 'match1', {});
hb.when('GET', '/url1').respond(200, 'match2', {});
hb.when('GET', '/url2').respond(204, 'nomatch', {});

callback.and.callFake(function(status, response) {
expect(status).toBe(200);
expect(response).toBe('match2');
});

hb('GET', '/url1', null, callback);

// Check if a newly added match is used
hb.when('GET', '/url1').respond(201, 'match3', {});

var callback2 = jasmine.createSpy();

callback2.and.callFake(function(status, response) {
expect(status).toBe(201);
expect(response).toBe('match3');
});

hb('GET', '/url1', null, callback2);
expect(callback).not.toHaveBeenCalled();
hb.flush();
expect(callback).toHaveBeenCalledOnce();
}
);
});


it('should respond with a copy of the mock data', function() {
var mockObject = {a: 'b'};

Expand Down