Angularjs - Test unit directive only numbers - angularjs

I have a directives.js file with two directives and I would like apply unit testing necesary, to improve coverage, but I do not know how to do it
This the directive.js file:
(function () {
'use strict';
angular
.module('app')
.directive('numbersOnly', function(){
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
var transformedInput = inputValue ? inputValue.replace(/[^\d.-]/g,'') : null;
if (transformedInput!=inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
}
})
.directive('numbersOnlyPositive', function(){
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
var transformedInput = inputValue ? inputValue.replace(/[^\d.]/g,'') : null;
if (transformedInput!=inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
}
});
})();
This is the tests file (directives.spec.js):
(function () {
'use strict';
describe('Unit testing directives general', function() {
var $compile,
$rootScope,
element,
vm;
// Load the app module, which contains the directive
beforeEach(module('app'));
// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
beforeEach(inject(function(_$compile_, _$rootScope_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$compile = _$compile_;
$rootScope = _$rootScope_;
vm.mesHastaSelected = "3";
vm.cantidadHastaSelected = "5";
//element = angular.element('<input text ng-model="mesHastaSelected">');
}));
it('should accept number input only', function() {
expect(vm.cantidadHastaSelected).toBe('1');
});
it('should accept number positive input only', function() {
expect(vm.mesHastaSelected).toBe('8');
});
});
})();
This is the html code input fields:
<input type="text" ng-model="vm.mesHastaSelected" maxlength="2" numbers-only-positive />
<input type="text" ng-model="vm.cantidadHastaSelected" maxlength="4" numbers-only />
Thanks for the help,

Related

How to write unit test case for scope and timeout function inside directive

I am using isolate scope in custom directive. I have updated plunker link. http://plnkr.co/edit/NBQqjxW8xvqMgfW9AVek?p=preview
Can someone help me in writing unit test case for script.js file.
script.js
var app = angular.module('app', [])
app.directive('myDirective', function($timeout) {
return {
restrict: 'A',
scope: {
content: '='
},
templateUrl: 'my-directive.html',
link: function(scope, element, attr) {
$timeout(function() {
element = element[0].querySelectorAll('div.outerDiv div.innerDiv3 p.myClass');
var height = element[0].offsetHeight;
if (height > 40) {
angular.element(element).addClass('expandable');
scope.isShowMore = true;
}
})
scope.showMore = function() {
angular.element(element).removeClass('expandable');
scope.isShowMore = false;
};
scope.showLess = function() {
angular.element(element).addClass('expandable');
scope.isShowMore = true;
};
}
}
})
(function() {
'use strict';
describe('Unit testing directive', function() {
var $compile, scope, element, compiledDirective, $rootScope, $timeout;
beforeEach(module("app"));
beforeEach(inject(function(_$compile_, _$rootScope_, _$timeout_) {
$compile = _$compile_;
scope = _$rootScope_.$new();
$timeout = _$timeout_;
element = angular.element(' <div my-directive content="content"></div>');
compiledDirective = $compile(element)(scope);
scope.$digest();
}));
it('should apply template', function() {
expect(compiledDirective.html()).toBe('');
});
it('check for timeout', function() {
$timeout.flush();
});
});
})();
Use $timeout.flush() function for writing testcase for $timeout
it('check for timeout', function() {
scope.digest();
// flush timeout(s) for all code under test.
$timeout.flush();
// this will throw an exception if there are any pending timeouts.
$timeout.verifyNoPendingTasks();
expect(scope.isShowMore).toBeTruthy();
});
Check this article for better understanding.

Errors thrown when unit testing directive with Angular

I have a custom directive that uses a service. When I run my unit tests, I keep getting the thrown error 'Library does not exist on window'. How can I avoid getting that error in my Unit test?
example service
angular.module('example')
.factory('thirdParty', ['$window', function($window) {
if (typeof $window.thirdParty === 'undefined') {
throw new Error('Library does not exist on window');
} else {
return $window.thirdParty;
}
}]);
custom directive
angular.module('example')
.directive('customDirective', ['thirdParty',
function(thirdParty) {
var defaults, link;
link = function(scope, element, attrs, ctrls) {
// do something with thirdParty
};
return {
restrict: 'A',
require: 'ngModel',
link: link,
};
}]);
test
describe('customDirective', function() {
var element, compile, scope;
beforeEach(module('example'));
beforeEach(inject(function($compile, $rootScope) {
compile = $compile;
scope = $rootScope.$new();
}));
// Manually compile and link our directive
function getCompiledElement(template) {
var compiledElement;
var validTemplate = '<input ng-model="example.data" custom-directive />';
compiledElement = compile(template || validTemplate)(scope);
scope.$digest();
return compiledElement;
}
it('should do something', function() {
element = getCompiledElement();
// expects
});
});
You need to inject $window (stub that out too, for good measure) and stub/mock out the thirdParty lib.
var $window;
beforeEach(function () {
$window = {};
module('yourmodule', function ($provide) {
$provide.value('$window', $window);
});
$window.thirdParty = {};
});
it('throws', function () {
delete $window.thirdParty;
function fn () {
getCompiledElement();
}
expect(fn).to.throw(/does not exist on window/i);
});
it('just works™', function () {
function fn () {
getCompiledElement();
}
expect(fn).to.not.throw;
});

Mocking formController in directive

I have a directive that checks for the validity of the form before doing something. It's working, but my unit tests keep on failing because I can't mock the formController in my karma tests.
I have the following directive:
.directive('directiveName', function() {
return {
restrict: 'A',
require: 'form',
link: function(scope, element, attrs, ctrl) {
element.bind('submit', function(event) {
if(!ctrl.$valid) ...do something here...
}
}
};
});
This is my test initialization:
var form, element, $scope;
beforeEach(inject(function($rootScope, $compile, $q) {
$scope = $rootScope.$new();
var template = angular.element('<form fl-submit="mockFunction()">' +
'<button type="submit"> </button> </form>');
$scope.mockFunction = function() {
return promise.promise;
};
element = $compile(template)($scope);
form = $scope.form;
}));
describe('and the form is not valid', function() {
form.$valid = false;
...expect something here...
});
Thanks very much for the help!

Unit testing Angular directive with $http

I have an Angular directive that, when attached to an <input>, waits one second after user input before querying an endpoint with $http. In short, it's meant to check username uniqueness.
It looks like this:
.directive('sgValidUsername', ['$http', function(http) {
var waitTimer;
var checkIfUserExists = function(e, ctrl) {
http.get('/publicapi/users/' + e.target.value + '/exists')
.success(function(data) {
ctrl.$setValidity('unique', !data.exists);
});
};
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
element.on('blur keyup', function(e) {
if (e.target.value) {
clearInterval(waitTimer);
waitTimer = setTimeout(function() {
checkIfUserExists(e, ctrl);
}, 1000);
}
});
}
};
}]);
I'm trying my best to write a good comprehensive Jasmine unit test suite, but it's not working out because I couldn't find an appropriate example to learn from. I end up reconstructing the directive in test form rather than actually testing the directive. Also, I get a 'no pending request to flush' error.
Any suggestions for the below?
describe('SignupForm directives', function () {
var form, // el to which directive is applied
$scope,
$httpBackend, // $http mock
usernameExistsHandler;
beforeEach(function() {
module('signupform.directives');
});
beforeEach(function() {
inject(function ($injector, $rootScope, $compile, $q, $timeout) {
$scope = $rootScope;
$httpBackend = $injector.get('$httpBackend');
usernameExistsHandler = $httpBackend.whenGET(/\/publicapi\/users\/.+?\/exists/);
var el = angular.element('<form name="form"><input type="text" name="username" ng-model="user.username" sg-username-is-valid /></form>');
$scope.user = { username: null };
$compile(el)($scope);
form = $scope.form;
});
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should invalidate with existing usernames', function() {
form.username.$setViewValue('username_in_use');
$scope.$digest();
expect($scope.user.username).toEqual('username_in_use');
usernameExistsHandler.respond('200', { exists: true });
$httpBackend.expectGET('/publicapi/users/' + $scope.user.username + '/exists/');
$httpBackend.flush();
expect(form.username.$valid).toBe(false);
});

Unit Test Directive with Isolated Scope

I want to unit test a directive which looks like this:
'use strict';
angular.module('app')
.directive('phone', function () {
return {
require: 'ngModel',
scope: {
user:'=',
},
link: function(scope, element, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
scope.user.division = null;
return viewValue;
});
}
};
});
Here is my failing test
'use strict';
describe('Directive: phone', function () {
// load the directive's module
beforeEach(module('app'));
var element,
scope;
beforeEach(inject(function ($compile, $rootScope) {
scope = $rootScope.$new();
element = angular.element('<form name="form"><input name="phone" ng-model="user.phone" phone></form>');
scope.user = {};
element = $compile(element)(scope);
element.scope().$digest();
}));
it('should work', function() {
scope.form.phone.$setViewValue('1');
});
});
But when I try to run it I get:
TypeError: 'undefined' is not an object (evaluating 'scope.user.division=null')
Your isolate scope implies that there should be a user attribute on the element that references a user entity on the parent $scope.
In you compiled HTML, though, there is no user attribute, therefore your isolate $scope's user property is undefined.
Your HTML should look like this:
<input ... user="user" />

Resources