I have a bookmarking app that takes in a url and automatically extracts a summary. When the client requests the server to add a new bookmark, the server sends back some initial information and initiates a process to extract the summary.
In the angular frontend, I have created directives for adding the bookmark and for managing each item in the bookmarks list. Within the listitem directive there is a checkSummary()
method that will poll the server to get the summary.
I am having problems with the unit test for this latter directive. It fails with "Unsatisfied request" but when I log to the console at various points the $http.get()
request seems to fire and I can see that $scope variables are updated so I don't really understand why it would fail with this cause. I've checked the answers to many different question but can't really find anything that would give some insight.
The code is the following:
bookmarks.js:
angular.module( 'userpages.bookmarks', [
'ui.router',
'ui.bootstrap',
'ui.validate'
])
.config(function config( $stateProvider ) {
$stateProvider.state( 'userpages.bookmarks', {
url: '/bookmarks',
templateUrl: 'userpages/bookmarks/bookmarks.tpl.html',
controller: 'BookmarksController',
data: { pageTitle: 'Bookmarks' },
});
})
.factory('bookmarksApiResource', ['$http', function ($http) {
var bookmarksUrl = '/api/v1/bookmarks/';
var api = {
getList: function() {
return $http.get( bookmarksUrl )
.then( function(response) { return response.data; });
},
getById: function(id) {
return $http.get( bookmarksUrl + id + '/' )
.then( function(response) { return response.data; });
},
addBookmark: function(articleUrl) {
return $http.post( bookmarksUrl, {article_url: articleUrl})
.then( function(response) { return response.data; });
},
checkSummary: function(id) {
return $http.get( bookmarksUrl + id + '/?fields=summary' )
.then( function(response) { return response.data; });
}
};
return api;
}])
.controller( 'BookmarksController', ['$scope', '$stateParams', 'bookmarksApiResource',
function BookmarksController( $scope, $stateParams, bookmarksApiResource) {
$scope.username = $stateParams.username;
$scope.templates = {
bookmarks: {
toolbar: 'userpages/bookmarks/bookmarks-toolbar.tpl.html',
listitem: 'userpages/bookmarks/bookmarks-listitem.tpl.html',
}
};
$scope.addBookmarkFormCollapsed = true;
$scope.bookmarks = [];
bookmarksApiResource.getList().then( function(data) {
$scope.bookmarks = data;
});
}])
.directive('newBookmark', ['bookmarksApiResource', function(bookmarksApiResource) {
var newBookmark = {
restrict: 'E',
templateUrl: 'userpages/bookmarks/bookmarks-add-form.tpl.html',
replace: true,
link: function($scope, $element, $attrs, $controller) {
$scope.addBookmark = function(articleUrl) {
var newBookmark = bookmarksApiResource.addBookmark(articleUrl);
$scope.bookmarks.push(newBookmark);
};
}
};
return newBookmark;
}])
.directive('bookmarksListitem', ['bookmarksApiResource', '$timeout',
function(bookmarksApiResource, $timeout) {
var listitem = {
restrict: 'E',
templateUrl: 'userpages/bookmarks/bookmarks-listitem.tpl.html',
replace: true,
scope: true,
link: function($scope, $element, $attrs, $controller) {
var checkSummary = function() {
if (!$scope.bookmark.summary_extraction_done) {
bookmarksApiResource.checkSummary($scope.bookmark.id).then(function(data){
if (data.summary_extraction_done) {
$scope.bookmark.summary_extraction_done = data.summary_extraction_done;
$scope.bookmark.summary = data.summary;
} else {
$timeout(checkSummary, 1000);
}
});
}
checkSummary();
}
};
return listitem;
}])
;
and the test is as follows:
bookmarks.spec.js
describe( 'userpages.bookmarks', function() {
var $rootScope, $location, $compile, $controller, $httpBackend;
var $scope, elem;
beforeEach( module( 'userpages.bookmarks', 'ui.router' ) );
... other tests ...
describe('bookmarks-listitem', function() {
var testBookmark;
beforeEach(module('userpages/bookmarks/bookmarks-listitem.tpl.html'));
beforeEach(inject(function(_$rootScope_, _$compile_, _$httpBackend_){
$rootScope = _$rootScope_;
$compile = _$compile_;
$httpBackend = _$httpBackend_;
testBookmark = {
id: 1,
title: 'Title of our first article',
url: 'http://www.example.com/some/article',
summary_extraction_done: false,
summary: '',
};
}));
it('when summary_extraction_done = false, checkSummary() should call the server and update the summary with the response', function () {
$httpBackend.when('GET', '/api/v1/bookmarks/1/?fields=summary').respond(200, {
summary_extraction_done: true,
summary: 'This is the summary for article 1.'
});
$httpBackend.expect('GET', '/api/v1/bookmarks/1/?field=summary');
$scope = $rootScope.$new();
$scope.bookmark = testBookmark;
elem = $compile('<bookmarks-listitem></bookmarks-listitem>')($scope);
$scope.$digest();
$httpBackend.flush();
expect($scope.bookmark.summary_extraction_done).toBe(true);
expect($scope.bookmark.summary).toBe('This is the summary for article 1.');
});
});
});
And the test results are:
Firefox 27.0.0 (Mac OS X 10.9) userpages.bookmarks bookmarks-listitem when summary_extraction_done = false, checkSummary() should call the server and update the summary with the respone FAILED
Error: Unsatisfied requests: GET /api/v1/bookmarks/1/?field=summary in .../frontend/vendor/angular-mocks/angular-mocks.js (line 1486)
createHttpBackendMock/$httpBackend.verifyNoOutstandingExpectation@.../frontend/vendor/angular-mocks/angular-mocks.js:1486
createHttpBackendMock/$httpBackend.flush@.../frontend/vendor/angular-mocks/angular-mocks.js:1464
@.../frontend/src/app/userpages/bookmarks/bookmarks.spec.js:6
Any suggestions would be helpful.
If you want to see what url httpBackend is using you can use following code to debug it.
$httpBackend.when("GET", new RegExp('.*')).respond(function(method, url, data) { console.log("URL:",url); });