Unit test for angularjs with $window.location.pathname - angularjs

I have a angularjs controller like this:
myApp.controller('SetCourseCtrl', function($scope, $http, $window,$sce) {
var path = $window.location.pathname;
path = path.split('/');
var courseID = path.pop();
$http.post('/course/getCourse', {
courseID: courseID
}).then(function(result) {
var trainerJSON = result.data.courseTrainer;
var trainers = [];
for (var i = 0; i < trainerJSON.length; i++) {
trainers.push(trainerJSON[i].text);
}
$scope.courseName = result.data.courseName;
$scope.courseTrainer = trainers;
$scope.courseTrainerPage = result.data.courseTrainerPage;
$scope.courseDescription = $sce.trustAsHtml(result.data.courseDescription);
$scope.courseCategory = result.data.courseCategory;
$scope.courseDocuments = result.data.courseDocuments;
});
});
I wrote a testcase for it by karma:
describe("SetCourseCtrl Unit testing #5", function() {
var controller, $controller, $scope, $rootScope, createController, $window;
beforeEach(angular.mock.module("myApp"));
beforeEach(inject(function($injector) {
$rootScope = $injector.get('$rootScope');
$controller = $injector.get('$controller');
$httpBackend = $injector.get('$httpBackend');
createController = {
setCourse: function() {
return $controller('SetCourseCtrl', {
$scope: $rootScope
});
}
}
}));
it('Test 2: post /course/getCourse', inject(function($controller) {
controller = createController.setCourse();
var path = $window.location.pathname;
path = path.split('/');
var courseID = path.pop();
var result = {
data: {
courseName: 'agile',
courseTrainerPage: 'trainer page',
courseDescription: 'description',
courseCategory: 'category',
courseDocuments: 'doc.pdf'
}
};
$httpBackend.whenPOST('/course/getCourse', {
courseID: courseID
}).respond(result);
$httpBackend.flush();
expect($rootScope.courseName).toBe(result.data.courseName);
expect($rootScope.courseTrainerPage).toBe(result.data.courseTrainerPage);
expect($rootScope.courseDescription).toBe(result.data.courseDescription);
expect($rootScope.courseCategory).toBe(result.data.courseCategory);
expect($rootScope.courseDocuments).toBe(result.data.courseDocuments);
}));
});
but when I run this test, an error happen:
PhantomJS 2.1.1 (Windows 8 0.0.0) SetCourseCtrl Unit testing #5 Test
2: post /course/getCourse FAILED
Error: Unexpected request: POST /course/getCourse
Can anyone explain it for me ? I think it happened because of the $window.location.pathname, I can not mock it, but I am not sure.

The error message said it, POST request to /course/getCourse is unexpected.
You need to insert an expect line like this:
$httpBackend.whenPOST('/course/getCourse', {
courseID: courseID
}).respond(result);
$httpBackend.expectPOST('/course/getCourse');
$httpBackend.flush();

Related

defining current state in unit tests

I've moved this 'reports' feature from a single module (called 'aam') into the core, so that other modules (such as 'bbc') can use it.
Now I'm rewriting the unit test(s).
The grunt error I'm getting is
should go state aam.reports with URL_NOT_SPECIFIED
reports-state spec
TypeError: 'null' is not an object
(evaluating 'BbpcConfiguration.getProperty(configProperty).then')
which indicates to me that $state is empty or not structured correctly.
Here is the report controller:
(function() {
'use strict';
angular.module('com.ct.bbpcCore')
.controller('reportController', ['$window', '$state', 'BbpcUserService', 'BbpcConfiguration', function ($window, $state, BbpcUserService,BbpcConfiguration) {
angular.element(document).ready(function () {
//Get url base on locale
var reportUrl = "URL_NOT_SPECIFIED";
var currentState = $state.current.name;
var configProperty = "";
var title = "";
if (currentState.indexOf('aam.reports')) {
configProperty = 'report.aam.link';
title = "AAM.REPORT";
};
if (currentState.indexOf('bbc.reports')) {
configProperty = 'report.bbc.link';
title = "BBC.REPORT";
};
BbpcConfiguration.getProperty(configProperty).then(function(response) {
if (response) {
var language = BbpcUserService.getLanguageCd() || "en_CA";
reportUrl = response[language] || reportUrl;
}
var spec = "width=" + $window.outerWidth + ", height=" + $window.outerHeight;
$window.open(reportUrl, title, spec);
});
});
}]);
}());
And here is report-controller.spec:
describe('reports-state spec', function() {
'use strict';
var $injector, $window, $rootScope,
$state, BbpcConfiguration, reportController, $controller, BbpcUserService;
beforeEach(function() {
module('com.ct.bbpcCore', function($provide) {
$provide.value('BbpcConfiguration', BbpcConfiguration = {
getProperty: function(key){
if('report.aam.link' === key){
return {
"fr_CA": "https://eng-link",
"en_CA": "https://fre-link"
};
}
return null;
}
});
});
inject(function(_$injector_) {
$injector = _$injector_;
$window = $injector.get('$window');
$state = $injector.get('$state');
$rootScope = $injector.get('$rootScope');
$controller =$injector.get('$controller');
BbpcUserService =$injector.get('BbpcUserService');
});
});
it('should go state aam.reports with URL_NOT_SPECIFIED', function() {
$state.current = {'name': 'aam.reports' };
spyOn($window, 'open').andCallFake(function(){});
reportController = $controller('reportController', {'$window':$window, '$state':$state, 'BbpcUserService':BbpcUserService, 'reportLink':undefined});
$state.go('aam.reports');
$rootScope.$apply();
expect($state.current.name).toEqual('aam.reports');
expect($window.open).toHaveBeenCalledWith('URL_NOT_SPECIFIED', 'AAM.REPORT', 'width=0, height=0');
});
});
I tried simply adding the line $state.current = {'name': 'aam.reports' }; in the 'it' block, but that's not what it's looking for.
Not sure how to debug unit tests. :P I can't use a console.log($state) to peek into it.

How to mock $window.top and $window.self?

In my controller, I am checking if page is iframed or not by this condition:
var vm = this;
if ($window.self !== $window.top) {
//We are iframed
vm.weAreIFramed = true
}
else {
vm.weAreIFramed = false;
}
I want to write a unit test for it. I wrote something like this but the test is always failing:
var homeController, $httpBackend, $controller, $location, $rootScope;
var topObj = {};
var selfObj = {};
var windowObj = {
location: { href: '' },
top: topObj,
self: topObj
};
beforeEach(module("app"), function ($provide) {
$provide.value('$window', windowObj);
});
beforeEach(inject(function (_$httpBackend_, _$controller_, _$location_, _$rootScope_) {
$httpBackend = _$httpBackend_;
$controller = _$controller_;
$location = _$location_;
$rootScope = _$rootScope_;
homeController = $controller("homeController");
}));
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it("vm.weAreIFramed should be false if we are not iFramed", function () {
console.log(angular.mock.dump(windowObj));
expect(homeController.weAreIFramed).toEqual(false);
});
It fails with below error:
How to write the correct test to check if the page is iframed or not ?
The test fails because "" === "". In real window, top and self are objects.
It should be
var topObj = {};
var selfObj = {};
var windowObj = {
location: { href: '' },
top: topObj,
self: selfObj
};
And for the other condition, only topObj can be specified for both.

Angular karma testing controller

I'm trying to figure out these karma tests to test my Angular app. I can figure out simple things. But I am having problems with one that connects to my database.
I am wanting to test the $scope.getMultipleOptions function in the controller below:
$scope.options = [];
$scope.getMultipleOptions("form_field_1", $scope.options);
$scope.getMultipleOptions = function (key, opts) {
var id = key.replace("form_field_", "");
var promise = DynamicFormFactory.GetData('/DynamicForm/GetMultipleOptions?form_field_id=" + id);
promise.then(
function (success) {
angular.forEach(success, function (o) {
opts.push(o);
});
},
function (error) {
// Error
}
);
}
This is what my factory/service looks like:
dynamicFormApp.factory('DynamicFormFactory', ['$http', function ($http) {
return {
GetData: function (url) {
return $http.get(url)
.then(function (response) {
return response.data;
}, function (error) {
return error;
});
}
}]);
And this is what I've done to set up my karma test
describe('DynamicFormController', function () {
beforeEach(module('dynamicFormApp'));
var $controller;
var $rootScope;
beforeEach(inject(function (_$controller_, _$rootScope_) {
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
$rootScope = _$rootScope_;
}));
describe('$scope.getMultipleOptions', function () {
var $scope, controller;
beforeEach(function () {
$scope = $rootScope.$new();
controller = $controller('DynamicFormController', { $scope: $scope });
$scope.fields = [];
});
it('$scope.getMultipleOptions', function () {
var key = "form_field_15";
var expectedResult = [{ desc: "DESCRIPTION", id: "ID" }];
$scope.getMultipleOptions(key, $scope.fields);
expect($scope.fields).toEqual(expectedResult);
});
});
});
The test always fails as $scope.fields is always empty. How do I do this?

JS Unit Testing Using karma and Jesmine

I have written a unit test like below
describe('modals', function() {
beforeEach(module('DetailsApp'));
var controller, rootScope, templateCache, compile, http, httpBackend;
var uibModalInstance = {
dismiss: function(message) {
},
close: function(message) {
}
};
var plugins = {
get: function(plugin) {
if(plugin == 'Workorder'){
return {
'workorder_id': 'workorder_id'
};
} else if(plugin == 'CompanyInfo'){
return {
'company_name': 'company_name',
'company_id': 'company_id'
};
}
}
};
beforeEach(module(function($provide) {
$provide.value('$uibModalInstance', uibModalInstance);
$provide.value('plugins', plugins);
}));
beforeEach(inject(function($controller, $templateCache, $compile, $rootScope, $http, $httpBackend) {
controller = $controller;
templateCache = $templateCache;
compile = $compile;
rootScope = $rootScope;
http = $http;
httpBackend = $httpBackend;
}));
describe('When modal functions are called', function() {
it('they should be called correctly', function() {
var $scope = {};
var companyRatingHistory = controller('companyRatingHistory', { $scope: $scope });
spyOn(uibModalInstance, 'dismiss');
spyOn(uibModalInstance, 'close');
$scope.cancel();
expect(uibModalInstance.dismiss).toHaveBeenCalledWith('cancel');
$scope.close('close');
expect(uibModalInstance.close).toHaveBeenCalledWith('close');
});
}); });
and found that my code coverage shows an E in plugins else block like below
else if(plugin == 'CompanyInfo'){
return {
'company_name': 'company_name',
'company_id': 'company_id'
};
}
What i have missed in my test. Advance thanks and get any suggestions from anybody who helps me.

Angular Function not Recognised in Unit Testing

I'm using angular-datatables in our project.
I got this error while testing:
TypeError: DTOptionsBuilder.newOptions(...).withBootstrap is not a function in /var/www/symfony/xxxx/src/XXXX/AdminBundle/Resources/public/js/controllers/category/category-grid.controller.js (line 20)
Which the code is recognised as function in dev code, and here is the testing code:
(function() {
"use strict";
describe('Category Grid Controller Unit Test', function() {
var $controller, $rootScope;
beforeEach(module('category-grid.controller'));
beforeEach(inject(function(_$controller_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
}));
it('should define category grid controller', function() {
var $scope = $rootScope;
var CategoryGridController = $controller('CategoryGridController', {$scope: $scope});
expect(CategoryGridController).toBeDefined();
});
});
})();
And here is the controller code:
function CategoryGridController($stateParams, $scope, Category, DTOptionsBuilder, DTColumnDefBuilder, Utils, $window, notify) {
$scope.categories = [];
$scope.dtOptions = DTOptionsBuilder.newOptions().withBootstrap();
$scope.dtColumnDefs = [
DTColumnDefBuilder.newColumnDef(5).notSortable()
];
if (angular.isDefined($stateParams.id)) {
Category.get({id: $stateParams.id}, function(data) {
var categories = data.children;
var translation;
for (var i = 0; i < categories.length; i++) {
translation = Utils.getTranslation(categories[i].translations);
categories[i].name = translation.name;
}
translation = Utils.getTranslation(data.translations);
$scope.categoryName = translation.name;
$scope.categories = categories;
});
}
else {
Category.query({ _level: 1 }, function(data) {
for (var i = 0; i < data.length; i++) {
var translation = Utils.getTranslation(data[i].translations);
data[i].name = translation.name;
}
$scope.categories = data;
});
}
$scope.toggleEnabled = function(id, index) {
console.log(!$scope.categories[index].enabled);
if ($window.confirm($scope.translations.ARE_YOU_SURE)) {
Category.patch({id: id}, {category: {enabled: !$scope.categories[index].enabled}},
function(data) {
$scope.categories[index].enabled = data.enabled;
notify({
messageTemplate: '<span><i class="fa fa-check"></i> ' + $scope.translations.DATA_SAVE_SUCCESS + '</span>'
});
},
function(e) {
notify({
messageTemplate: '<span><i class="fa fa-warning"></i> ' + $scope.translations.DATA_SAVE_ERROR + '</span>',
classes: 'alert-danger'
});
}
);
}
};
}
The controllers code work in development. If I remove the line with DTOptionsBuilder.newOptions(...).withBootstrap, the testing code works. But I need that code in development.
Since you are performing unit testing of controller, all dependencies on the controller need to mocked and injected, including your DTOptionsBuilder.
This dependency can be mocked by either creating you own custom objects and injecting them in the test or using jasmine spy.Read documentation around how to create spy in Jasmine.
For example in your test you can create
var DTOptionsBuilder={
newOptions:function() {
return {
withBootstrap :function() {}
};
}}
and inject this object when you instantiate the controller
var CategoryGridController = $controller('CategoryGridController', {$scope: $scope,'DTOptionsBuilder':DTOptionsBuilder});
The bottom line is if you controller is dependent upon any other service, either a real or mock implementation of that service has to be inject for controller to work, allowing you to unit test it.

Resources