Angular $httpBackend test doesn't capture $http call - javascript

I'm using angular's $httpBackend for unit testing a bunch of API functionality. The application makes use of $resource for most API endpoints, but has a single raw $http call (to send data along with a DELETE request, not my choice).
The model has a simple delete method like this:
this.delete = function(target) {
return $http({method: 'DELETE', url: '/api/delete' + this.id, data: target});
};
And a simple test case, which fails with No pending request to flush:
it('calls the API', function() {
httpBackend.expect('DELETE', '/api/delete').respond(200, '');
scope.obj.delete(1);
httpBackend.flush();
});
This test setup seems to work fine with any endpoints using $resource, but fails as soon as I use $http. I verified that the .delete() method has been called, and that it returns a promise as expected.
Did I miss something in the documentation?

Related

Initialize Current User Service on Application Start in AngularJS

I’m developing a Single Page Application with AngularJS.
When a user successfully logs in, a security token is stored in a cookie. Now, when he refreshes the page, the token will be sent to the backend, which returns a JSON object "currentUser" containing all the relevant information about the current user (as name, access-groups, profile picture, etc.).
The problem is, this is an asynchronous process of course, so when the controller starts another operation, say, just alerting the user’s name, this value will be undefined at that time.
Of course, I could set a timeout but is there a better solution?
I thought about a "currentUserService", which initializes first (sending the cookie and filling the user information with the backend response) and can only be processed after this initialization is completed.
But how can this be done? Or are there any other possibilities?
edit:
Hi guys,
thanks for the input!
Both of your suggestions seem to be very promising for asynchronous requests in general, but I think they might not fit perfectly for my concern:
The information about the current user only have to be requested once, so I would like to store them for the whole application (e.g. in the rootScope or a service) accessible from any controller without having to request them again in every controller (as in the callback or resolve-solution) but make sure that there won’t be any „timeout“ problems. Do you have any ideas?
You can resolve the user's data before the view loads either with ng-route or ui-router:
This example is written for ui-router:
.state('profile', {
url: '/profile',
controller: 'profileCtrl as vm',
resolve: {
user: function(AuthService) {
//Return a promise or an object to be resolved.
return AuthService.getUserFromToken(); //Say this is asynchronous and returns a promise
}
}
});
//In controller:
.controller('profileCtrl', function(... , user) {
//User data available here
this.user = user;
});
Please note if any errors arise during the resolve stage the state will not be loaded so you'll have to take care of the errors!
If a user refreshes you have to initialize everything. I assume the token is stored in localstorage or something and I assume this is angular 1.*. To do this I think you should call user-related functions from your http call-callback:
$scope.user = {};
$scope.getUser = function(){
$http({
method: 'GET',
url: '/someUrl'
}).then(function (res) {
$scope.user = res.data; //or whatever the response is
$scope.handleUserRelatedThings();
}).catch(function(err) {
//handle error
})
}
$scope.handleUserRelatedThings = function(){
//do things with $scope.user
alert($scope.user.name);
}
//on init
$scope.getUser();

Using $resource with rest service

I'm trying to get familiar with $resource in order to use RESTful Web Services.
So, for the attempt, I declared a factory like this :
'use strict';
angular.module('BalrogApp').factory('Req', ['$resource', function($resource) {
return $resource('http://localhost:8000/api/catalog/requests/:id', {id: '#id'}, {
'update': { method: 'PUT'},
'query': { method: 'GET'}
});
}]);
In my Controller I have this :
angular.module('BalrogApp').controller('requestsController', function(Req, $route, $rootScope, $scope, $location, $routeParams) {
this.$route = $route;
var controllerScope = this;
this.r = Req.get({ id:4 });
console.log(this.r);
This works fine, in my console, I can see my object with the data retrieved from the services for the item with id 4.
However I'm not sure I'm properly retrieving the data since the object i'm storing in r contains an id like it should in the object details (clicking on "Object" in the browser console displays the object details including its id) but the object itself (r) displayed on the console contains only 2 fields and is presented as follows : Object { $promise: Object, $resolved: false }.
So, in the end, console.log(this.r); works but console.log(this.r.id); doesn't even if r is supposed to contain an id just like in the object details from the browser inspector.
Also, how can I configure this service in order to be able to use get() with the id parameter just like I'm doing which results in calling http://localhost:8000/api/catalog/requests/:id but also without the id parameter which results in calling http://localhost:8000/api/catalog/requests
As the call to the REST API is asynchronous you shout wait for the returned promise to resolve when using the Req.get({ id:4 });
Use:
Req.get({ id:4 }).$promise.then(function(result){
console.log(JSON.stringify(result));
});
instead.
For the second part of your question: Using data without an id property should be fine. This data, however, will be transfered in the request body and not as a request parameter.

AngularJS: Error in resource configuration for action `query`. Expected response to contain an object but got an array

I am trying to call a REST service using Angular 1.3 but keep getting an "Error: error:badcfg
Response does not match configured parameter".
I suspect it is in my controller where I call the $scope.data. I believe .data is correct but it is throwing this error.
Here is my service, including a test REST call:
var pfcServices = angular.module('pfcServices', ['ngResource'])
pfcServices.factory('pfcArticles', ['$resource',
function($resource){
return $resource('https://myrestcall.com/data, {}, {
query: {method:'GET'}
});
}]);
Here is my controller:
var pfcControllers = angular.module('pfcControllers', []);
pfcControllers.controller('pfcCtrl', ['$scope', 'pfcArticles', function ($scope, pfcArticles) {
$scope.data = pfcArticles.query();
}]);
Within IE, I get a CORS message of: XMLHttpRequest for https://pfc.azure-mobile.net/tables/articles required Cross Origin Resource Sharing (CORS). This does not occur within Chrome.
However, I am not sure if this is related, or just a bad call on my part. I have added my test site to the CORS in Azure Mobile Webservices where I am hosting the test REST call.
I am newer to Angular, so I am leaning towards a bad call on my part.
I am not sure why have set query properties on the resource. Either remove the configuration for query
return $resource('https://pfc.azure-mobile.net/tables/articles', {});
or set isArray true on the query configuration.
return $resource('https://pfc.azure-mobile.net/tables/articles', {}, {
query: {method:'GET',isArray:true}
});
The error is coming because Angular is not able to deserialize your response as it expects an object but the response from the call is an array.
The query method on $resource already has this flag set, but since you are redefining the query configurations this error is occurring. Do check the $resource documentation.

Calling Angular service from outside JS via .scope().call() hangs request

I'm working on adding a Google+ signin button to my Angular app and most of it is working, except for the handling of the callback result. The callback from the G+ signin is an outside JS function called signinCallback with looks like so:
//Handling the Google+ Signin right here
function signinCallback(authResult) {
angular.element($("#btnGooglePlus")).scope().handleGoogleSignin(authResult);
}
The only way I could figure out how to pass the authResult back into the controller was to call a controller method via element.scope(). handleGoogleSignin is called fine, and inside that function there is a http.get service call that looks like:
User.getSocialKey(key).then(function(data) {
console.log(data);
});
User is a service, and getSocialKey looks like:
getSocialKey: function(etag) {
console.log("Hit the social key service with the etag: " + etag);
return $http({
url: '/api/user/social',
method: 'post',
data: {etag:etag}
}).then(function(result) {
console.log("Returning promise from social service");
return result.data;
});
},
The first log statement gets hit fine, then nothing. Request is never sent. Now, if I go and click something on the page that has an ng-model attribute (example, a checkbox), the request is then sent and received just fine. So my question: Why is my Angular service call being suspended until I click on something? Why isn't it going through right away?
I've tried replacing getSocialKey with working service calls, same thing. I believe the issue comes down to calling the function with angular.element($("#btnGooglePlus")).scope().handleGoogleSignin(authResult); but I'm not sure. Anyone seen this before?
Sorry I can't test but I think you should call .$apply() since the action is triggered outside the AngularJS's scope.
function signinCallback(authResult) {
angular.element($("#btnGooglePlus")).scope().handleGoogleSignin(authResult);
angular.element($("#btnGooglePlus")).scope().$apply();
}

Is it possible for ngResource to be called synchronously?

I currently have a factory which I'm using to retrieve a config file.
m.factory('clientConfig', function($resource) {
var r;
r = $resource('assets/config.json', {}, {
query: {
method: 'GET'
},
isArray: false
});
return r.query();
});
The config file is a json file which contains the location of a nodeJS server. In my local environment, the json file is
{
"serverURL": "http://localhost\\:3000"
}
When I start my app on the front page this is fine. The front page loads the clientConfig module, and any subsequent page just uses the clientConfig module like below without any problem
m.factory('House', function($resource, clientConfig) {
return $resource(clientConfig.serverURL + '/houses/:houseId',
...
The problem I'm running into is if I enter the site on a page that immediately wants to load data from the server. In that case, because clientConfig is not populated yet and still empty and this stuffs up my $resource(clientConfig.serverURL + '/houses/:houseId' call.
My question is is it possible to load up clientConfig synchronous or at least have my app not start until after clientConfig has been populated?
You can't. Since JavaScript is (mostly) single threaded, there are very very few blocking operations. XHR is not one of those. There is no way to make it synchronous.
Depending on your angular version, you can either $then or $promise it:
clientConfig.$then(function (){ do something})
or
clientConfig.$promise.then(function () {do something else})
You can't. $resource always returns asynchronous data. If you want synchronous data then use $http instead of $resource.
Change your service like this;
m.factory('clientConfig', function($http) {
var get = function {
$http.get('assets/config.json').success(function(data)){
return data.serverURL;
});
};
});
And call the service like;
clientConfig.get();

Categories

Resources