Test directive that sets focus - angularjs

Directive:
angular
.module('tdsapp')
.directive('focus', focus);
focus.$inject = ['$timeout'];
// Used to set focus on element
function focus($timeout) {
return {
scope: { focus: '#'},
link: function (scope, element) {
scope.$watch('focus',
function (value) {
if (value) {
$timeout(function () {
element[0].focus();
console.log('focus called');
});
}
}
);
}
};
Test Spec:
describe('directive: focus', function () {
var $timeout, element, $scope, $compile;
beforeEach(function () {
module('tdsapp');
inject(function (_$timeout_, $rootScope, _$compile_) {
$timeout = _$timeout_;
$compile = _$compile_;
$scope = $rootScope.$new();
});
});
it('should call focus on element', function () {
var elm = angular.element('<input type="text" name="second" focus="true" />');
spyOn(elm[0], 'focus');
$timeout.flush();
expect(elm[0].focus).toHaveBeenCalled();
});
});
I have looked at AngularJS: how to test directive which gives focus to element? and at How do I check if my element has been focussed in a unit test and neither works. I either get the expect failing or $timeout.flush() - No deferred tasks to be flushed.
EDIT:
As I stated in the text - the above two examples do not work.

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.

Unit Testing Directive Scope Function After Broadcast

I have a simple directive, and in the link function is a $broadcast receiver $on. Once received, a scope function is called:
return {
restrict: 'E',
link: function($scope, element, attrs) {
$scope.createCart = function () {
// Create cart
}
function getProductList() {
// get products lists
}
$scope.$on('create-shopping-cart', function() {
$scope.createCart();
getProductList();
});
}
};
I have tried:
describe('<-- myDirective Spec ------>', function () {
var scope, $compile, element, $httpBackend;
beforeEach(angular.mock.module('myApp'));
beforeEach(inject(function (_$rootScope_, _$compile_, _$httpBackend_) {
scope = _$rootScope_.$new();
$compile = _$compile_;
$httpBackend = _$httpBackend_;
var html = '<my-directive"></my-directive>';
element = $compile(angular.element(html))(scope);
spyOn(scope, '$on').and.callThrough();
scope.$digest();
}));
it('should be defined', function () {
expect(element).toBeDefined();
});
it('should broadcast ', function () {
scope.$broadcast('create-shopping-cart');
scope.$digest();
expect(element.isolateScope().createCart()).toHaveBeenCalled();
});
});
With the above, i see error:
TypeError: 'undefined' is not an object (evaluating 'element.isolateScope().createCart')
How can i test $scope.createCart(); AND getProductList(); are called?

how can i test custom directive in jasmine

i'm trying to write test case for custom directives with Jasmine. Currently, I have a directive that is setup as shown here:
This is my code in fiddler
http://jsfiddle.net/aHmPV/33/
app.directive('focusMe', function ($timeout) {
return {
scope: { trigger: '=focusMe' },
link: function (scope, element) {
scope.$watch('trigger', function (value) {
if (value === true) {
element[0].focus();
}
});
}
};
});
My Jasmine code is in fiddler
I tried different ways to test $watch function unable to achieve it , please help
You are set $watch on scope.trigger, but it is passed in focus-me.
You can use spyOn to element[0].focus and create test for focus-me="true" and focus-me="false":
describe('Directive: focusMe', function() {
var element,
scope;
beforeEach(module('CommonBusInfo'));
beforeEach(inject(function($rootScope) {
scope = $rootScope.$new();
}));
it("should focus if focus-me is true", inject(function($compile) {
element = angular.element('<input type="text" focus-me="true" value="chaitu">');
element = $compile(element)(scope);
spyOn(element[0], 'focus');
scope.$digest();
expect(element).toBeDefined();
expect(element[0].focus).toHaveBeenCalled();
}));
it("should NOT focus if focus-me is falsy", inject(function($compile) {
element = angular.element('<input type="text" focus-me="" value="chaitu">');
element = $compile(element)(scope);
spyOn(element[0], 'focus');
scope.$digest();
expect(element).toBeDefined();
expect(element[0].focus).not.toHaveBeenCalled();
}));
});

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;
});

Angular Directive Behavior Testing Jasmine

Here's Plunker Link
Updated Plunker Updated Plunker
I have written a directive which hide and shows the div based on ajax call.
How i can i test a behaviour of Directive written ?Its shows the spinner on Ajax Call.
Spec
describe('loader directive', function() {
var $compile, scope;
beforeEach(module('plunker'));
beforeEach(inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
scope = _$rootScope_.$new();
}));
it('check if div shows on', function() {
var element = $compile('<div loading>Something</div>')(scope);
scope.$apply();
});
});
Directive
app.directive('loading', ['$http' ,function ($http)
{
return {
restrict: 'A',
link: function (scope, elm, attrs)
{
scope.isLoading = function () {
return $http.pendingRequests.length > 0;
};
scope.$watch(scope.isLoading, function (v) {
if(v) {
elm.show();
} else {
elm.hide();
}
});
}
};
}]);
You can just check for an .is(':visible').
describe('loader directive', function() {
var $compile, scope, $httpBackend, $http, element;
beforeEach(module('plunker'));
beforeEach(inject(function(_$compile_, _$rootScope_, _$httpBackend_, _$http_) {
$compile = _$compile_;
scope = _$rootScope_.$new();
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', '/test').respond(200, {
test: true
});
$http = _$http_;
}));
afterEach(function() {
$httpBackend.flush();
if (element && element.length) {
element.remove();
}
});
it('check if div shows on http call', function() {
element = $compile('<div class="loading-bar" loading>Something</div>')(scope);
element.appendTo('body');
$http.get('/test');
scope.$apply();
expect(element.is(':visible')).toBe(true);
});
});
To mock out the $http calls you will need to use $httpBackend service.
Plunker Demo

Resources