Angular JS-Jasmine how to unit test $location.path().search()? - angularjs

been doing some googling but cant find an answer to this little problem of mine. Just trying to test this bit of code from my controller:
$scope.viewClientAssets = (id) ->
$location.path("/assets").search("client_id", "#{id}")
which in turn returns this url:
http://localhost:3000/admin#/assets?client_id=19
this all works fine however when unit testing......A few assumptions:
I have my spec set up correctly as I have other tests and expectations working perfectly fine so here is the test:
it 'checks if view client assets path is correct', ->
createController()
#gets rid of unnecessary function calls
flushRequests()
#calls the ctrl function with necessary args
$scope.viewClientAssets(clientData.client.id)
spyOn($location, 'path')
spyOn($location, 'search') #tried diff methods here such as
# and.returnValue(), .calls.any(), calls.all()
expect($location.path).toHaveBeenCalledWith("/assets")
expect($location.path.search).toHaveBeenCalled()
all other tests pass however when this gets hit I get the following error:
TypeError: Cannot read property 'search' of undefined
the console debugger tells me:
$scope.createClientAsset = function(id) {
return $location.path('/assets/create').search("client_id", "" + id);
};
that path is undefined?
any ideas anyone?

To test the spyOn, for search, do the following
spyOn($location, 'path').and.callThrough();
spyOn($location, 'search');
That will return values correctly.

You are spying on $location.path and not returning anything, or returning undefined.
Make your spy return a string (the string you would normally expect, the actual path) and the search function, defined for all strings in javascript, will work normally in your test.
e.g. Do as follows:
spyOn($location, 'path').and.returnValue('/client_id/')
$location.path('anything'); // returns '/client_id/'

Related

TypeError: undefined is not a constructor in jasmine unit test with angularjs

I am having above error in my Jesmine tests. Code has a Controller which calls AppsService service to receive a promise response of array of apps followed by onLoad method -
AppsService.initAndGetApps(appCredentialType)
.then(onLoad)
.catch(onError);
Code works fine but getting error for unit tests like -
TypeError: undefined is not a constructor (evaluating 'AppsService.initAndGetApps(appCredentialType).then(onLoad)')
I tried mocking initAndGetApps in my spec file with Jasmine & also as custom method like this (both are giving same above error) -
1.
initAndGetApps : jasmine.createSpy("initAndGetApps").and.callFake(function (credentialType) {
return []; // empty is ok
}
2.
AppsService = {
initAndGetApps: function (credentialType) {
return [];
}
}
$provide.value('AppsService', AppsService);
AppsService uses deferred.promise to return promise response after doing some computation based on credentialType parameter (It also does two Promise calls). Test is not able to call initAndGetApps as not getting console of credentialType at first line of initAndGetApps.
Can someone guide me the right way to mock AppsService.
You are not returning a promise in none of those two mocks. I would try if something along these lines:
initAndGetApps : jasmine.createSpy("initAndGetApps").and.returnValue(
new Promise(function(resolve, reject) {
// not taking our time to do the job
resolve(desiredReturnValue);
}
);
You can change the return value to a mock value so your code works.

Angular unit Testing spyOn().and.callthrough doesn't call actual funciton

I am new in unit testing with Jasmine in Angular.
I am currently testing a service that has a function called loadSomething(id) and I have added a console.info in it.
MY SERVICE:
function loadSomething(id)
{
console.info('this is a test message');
return (a promise from a POST request)
}
And this is my test (spec) file:
//verify that the load function exists
it('load snapshot',function(){
expect(MyService.loadSomething(108)); //statement 1
spyOn(MyService, 'loadSomething').and.callThrough(); //statement 2
});
So, I read online that the callthrough method of SpyOn calls the ACTUAL function.
However, when i run my test with the expect statement (statement 1) the console.info message is invoked (that works fine). On the other hand, when I comment out statement 1 and uncomment the SpyOn(statement 2) I do not get the console.info message anymore.
I would expect the exact opposite to happen.
Have I understood something wrong here?
(the rest of the code works fine, both the spec file and the actual service, I just don't really get this specific thing)
The SpyOn will help you to setup how the function should react when it's being called upon in your tests. Basically it's jasmines way of creating mocks.
In your case you have defined what the test should do when the service function is being called, which is callThrough.
The problem is that you also need to act on the service function (or the scope function which calls your service method) in order
to trigger the SpyOn which will callThrough
it('load snapshot',function(){
//setup
spyOn(MyService, 'loadSomething').and.callThrough(); //statement 2
//act
//either call the scope function which uses the service
//$scope.yourServiceCallFunction();
//or call the service function directly
MyService.loadSomething(1); //this will callThrough
});
Here's an simple test where we will mock the response of the SpyOn to a string
it('test loadSomething',function(){
//setup
spyOn(MyService, 'loadSomething').and.returnValue('Mocked');
//act
var val = MyService.loadSomething(1);
//check
expect(val).toEqual('Mocked');
});

$scope variable is undefined when it is set inside a function

I have the following example code in my learning app. The service does his job and pulls some data out of a page with json code generated by php, so far so good.
service:
(function() {
'use strict';
angular
.module('app.data')
.service('DashboardService', DashboardService);
DashboardService.$inject = ['$http'];
function DashboardService($http) {
this.getFormules = getFormules;
////////////////
function getFormules(onReady, onError) {
var formJson = 'server/php/get-formules.php',
formURL = formJson + '?v=' + (new Date().getTime()); // Disables cash
onError = onError || function() { alert('Failure loading menu'); };
$http
.get(formURL)
.then(onReady, onError);
}
}
})();
Then i call the getFormules function in my controller and put all the data inside my $scope.formuleItems and test if everything succeeded and 'o no'... $scope.formuleItems = undefined! - Strange because my view is showing data?
part of the controller:
dataLoader.getFormules(function (items){
$scope.formuleItems = items.data;
});
console.log('+++++++++++++++++', $scope.formuleItems); // gives undefined
The first thing i did was search around on stackoverflow to look if someone else had the same issue, and there was: Undefined variable inside controller function.
I know there are some walkarounds for this, i've done my own research, but something tells me that this (see example below) isn't the best way to solve this problem.
solution one: put $watch inside of the controller
$scope.$watch('formuleItems', function(checkValue) {
if (checkValue !== undefined) {
//put the code in here!
}
}
or even:
if($scope.formuleItems != null) {}
The rest of the controller is relying on $scope.formuleItems. Do i really have to put everything into that $watch or if? Can i fix this with a promise? I never did that before so some help would be appreciated.
The code in your callback
function (items){
$scope.formuleItems = items.data;
}
is evaluated asynchronously. That means you first fire the request, then javascript keeps on executing your lines of code, hence performs
console.log('+++++++++++++++++', $scope.formuleItems); // gives undefined
At this point the callback was not invoked yet, because this takes some time and can happen at any point. The execution is not stopped for this.
Therefore the value of $scope.formuleItems is still undefined, of course.
After that - at some not defined time in the future (probably a few milliseconds later) the callback will be invoked and the value of $scope.formuleItems will be changed. You have to log the value INSIDE of your callback-function.
You urgently have to understand this concept if you want to succeed in JavaScript, because this happens over and over again :)

AngularJS testing $httpBackend.whenGET().respond() not working with number as parameter

This test:
it("getFooCount success", function () {
httpBackend.whenGET('/api/getFooCount').respond(1);
fooService.getFooCount();
httpBackend.flush();
expect(fooService.FooCount).toBe(1);
});
fails: "Expected null to be 1"
(fooService.fooCount is null before the api get method is called).
Yet this test passes:
it("getFooCount success", function () {
httpBackend.whenGET('/api/getFooCount').respond("1");
fooService.getFooCount();
httpBackend.flush();
expect(fooService.fooCount).toBe("1");
});
It seems to be able to pass the test with anything (an object, string, array, etc) EXCEPT a number in the .respond().
The api I am calling just returns an integer, any advice as to how I can get this to work, and/or why it is failing?
Have a look at the API for respond, because the first argument is actually the status code.
Try instead respond(200, 1).
Angularjs seems to smart its way around when you provide a non statist object as first parameter

How do I test AngularJS trusted html with Jasmine?

I would like to use Jasmine to ensure an html data value has been correctly trusted by AngularJS.
Code
The code below fetches an article via an external api and uses Angular's $sce to trust the html content held in article.Body.
getArticle: ->
defer = #$q.defer()
#getContent(url).then (result) =>
article = result.data
article.Body = #$sce.trustAsHtml article.Body
defer.resolve article
defer.promise
This code works and, as I step through it, I can see the data is returned and the html property article.Body has been correctly trusted. Now I would like to write a unit-test that confirms this.
Unit-Test
Here is my attempt at the jasmine unit-test:
describe 'when getArticle is called with valid articleId', ->
it "should call getContent and return article with trusted html", ->
getContentDefer = #$q.defer()
spyOn(#contentService, 'getContent').andReturn getContentDefer.promise
article = {Id:'1', Body: '<div>test</div>'}
#contentService.getArticle(article.Id).then (response) =>
expect(response.Body instanceof TrustedValueHolderType).toBeTruthy()
getContentDefer.resolve {data:article}
#$rootScope.$digest()
You can see that I am attempting to ensure that the returned response.Body is an instance of the AngularJS type: TrustedValueHolderType. I do not know if this is a good idea but anyway, it does not work and I receive the following error:
ReferenceError: TrustedValueHolderType is not defined
I was hoping there was a neat way, perhaps a boolean flag, that I could use to determine whether the article.Body is trusted html or just a plain html string.
Update
The accepted answer below (thanks #avowkind) gave me the hint I needed. The trick is to use the $sce.getTrustedHtml() method which takes the TrustedValueHolderType and returns the original html value. Perfect.
Passing Unit-Test
ddescribe 'getArticle', ->
it "should return an article with trusted html", ->
getContentDefer = #$q.defer()
spyOn(#contentService, 'getContent').andReturn getContentDefer.promise
body = '<div>test</div>'
article = {Id:'1', Body: body}
#contentService.getArticle(article.Id, #token).then (response) =>
expect(#$sce.getTrustedHtml(response.Body)).toEqual(body)
I am able to jasmine unit test my filter by using $sce.getTrustedHtml on the output of the filter. This works fine if you know how to inject the $sce service into the test.
e.g
/**
* A filter used to wrap code in the <pre> tag
*/
myApp.filter( 'code', ['$sce', function($sce) {
return function(input) {
var html = (input != "")? '<pre>' + input + '</pre>' : input;
return $sce.trustAsHtml(html);
};
}]);
// test output
it('wraps pre around input: ', function() {
expect($sce.getTrustedHtml(codeFilter("Hello"))).toEqual("<pre>Hello</pre>");
} );
This works for my local system tests. However I tried to build an example fiddle with it
http://jsfiddle.net/avowkind/vfWr3/1/
and this returns an error:
Unknown provider: $sceProvider <- $sce
if anyone can fix the fiddle that would be great.

Resources