check attribute from html - angular directive testing jasmine karma - angularjs

how to check attribute is present in HTML and match its value. this is a test spec.js I wrote,
define(['angular',
'angularMocks',
'site-config',
'ng_detector',
],
function(angular,
mock,
$app,
ng_detector) {
describe('ng-detector controller', function() {
beforeEach(angular.mock.module("webapp"));
var $compile, $rootScope, tpl, $scope, elm, templateAsHtml;
beforeEach(angular.mock.inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
// $scope = _$rootScope_.$new();
}));
it('should initialize the ng-detector directive', inject(function() {
var tpl = $compile("<div ng-detector ></div>")($rootScope);
$rootScope.$digest();
console.log(tpl) // Log: r{0: <div ng-detector="" class="ng-scope" ng-verison="1.6.4"></div>, length: 1}
templateAsHtml = tpl[0].outerHTML;
expect(templateAsHtml.attr('ng-version')).toEqual(angular.version.full);
}));
});
});
directive. that adds angular version to attribute ng-version
'use strict';
define(['app-module'], function(ng) {
$app.info('ng detector initialized. {file: directives/ng-detector.js}');
ng.directive('ngDetector', function() {
return {
restrict: "A",
link: function(scope, elm, attr) {
elm.attr('ng-version', angular.version.full);
}
};
});
return ng;
});
I want to get a ng-version attribute set by the directive and match the attribute value.

I figured out myself. I was looking at the different place.
it('should check the angular version number', angular.mock.inject(function() {
expect(tpl.attr('ng-version')).toEqual(angular.version.full);
}));

Related

unit testing directive with link using controller

I'm trying to unit test my directive that set form validity depending on a controller variable.
My directive code :
angular.module('myModule',[])
.directive('myDirective', function() {
return {
restrict: 'A',
link: function(scope, element, attr, ctrl) {
scope.$watch("mailExist", function(){
if(scope.mailExist) {
ctrl.$setValidity('existingMailValidator', false);
} else {
ctrl.$setValidity('existingMailValidator', true);
}
});
}
};
});
When trying to unit test this directive, I'm trying to isolate the controller ctrl with this code:
describe('directive module unit test implementation', function() {
var $scope,
ctrl,
form;
beforeEach(module('myModule'));
beforeEach(inject(function($compile, $rootScope) {
$scope = $rootScope;
var element =angular.element(
'<form name="testform">' +
'<input name="testinput" user-mail-check>' +
'</form>'
);
var ctrl = element.controller('userMailCheck');
$compile(element)($scope);
$scope.$digest();
form = $scope.testform;
}));
describe('userMailCheck directive test', function() {
it('should test initial state', function() {
expect(form.testinput.$valid).toBe(true);
});
});
});
Running this test, I still obtain:
Cannot read property '$setValidity' of undefined
that's mean I haven't really inject a controller.
What is wrong in my test?
Finally in found the solution:
first in code I have add :
require: 'ngModel',
and then modified the unit test as follow:
describe('directive module unit test implementation', function() {
var scope,
ngModel,
form;
beforeEach(module('myModule'));
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
var element =angular.element(
'<form name="testform">' +
'<input name="testinput" ng-model="model" user-mail-check>' +
'</form>'
);
var input = $compile(element)(scope);
ngModel = input.controller('ngModel');
scope.$digest();
form = scope.testform;
}));
describe('userMailCheck directive test', function() {
it('should test initial state', function() {
expect(form.testinput.$valid).toBe(true);
});
});
});
and everything works fined.

Angular $scope property undefined

I'm trying to set the $scope.sang property to an instance of SangService in a directive. Problem is, it doesn't appear to be getting set.
The Setup
sang-player.directive.js:
angular.module('sang').directive('sangPlayer', [
'SangService',
function(SangService) {
return {
restrict: 'EA',
scope: true,
link: function(scope, _elem, attr) {
window.console.log('link');
scope.sang = SangService.$new();
}
};
}]);
sang-player.directive.spec.js:
describe('The Sang Directive', function() {
var compile, scope, element, sang;
beforeEach(function() {
sang = jasmine.createSpyObj('sang',
['play', 'pause', 'playPause', 'previous', 'next', 'seek']);
module(function($provide) {
$provide.value('Sang', sang);
});
inject(function($compile, $rootScope) {
compile = $compile;
scope = $rootScope.$new();
element = getCompiledElement();
});
});
function getCompiledElement() {
var element = angular.element('<div sang-player></div>');
var compiledElement = compile(element)(scope);
scope.$digest();
return compiledElement;
}
it('creates a scope.sang object', function() {
expect(scope.sang).toBeDefined();
expect(typeof scope.sang).toBe('object');
});
});
Output from failed Jasmine spec run:
The Sang Directive
X creates a scope.sang object
Expected undefined to be defined. (1)
Expected 'undefined' to be 'object'. (2)
Not sure why there's no output from the link callback. I would expect this to get called within compile(element)(scope) or scope.$digest().
The Deets
Angular 1.4.1
Jasmine 2.4.1
grunt-contrib-jasmine 0.9.1
The full project repo lives here if you want to tinker.

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.

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

isolateScope() returns undefined when using templateUrl

I have a directive that I want to unittest, but I'm running into the issue that I can't access my isolated scope. Here's the directive:
<my-directive></my-directive>
And the code behind it:
angular.module('demoApp.directives').directive('myDirective', function($log) {
return {
restrict: 'E',
templateUrl: 'views/directives/my-directive.html',
scope: {},
link: function($scope, iElement, iAttrs) {
$scope.save = function() {
$log.log('Save data');
};
}
};
});
And here's my unittest:
describe('Directive: myDirective', function() {
var $compile, $scope, $log;
beforeEach(function() {
// Load template using a Karma preprocessor (http://tylerhenkel.com/how-to-test-directives-that-use-templateurl/)
module('views/directives/my-directive.html');
module('demoApp.directives');
inject(function(_$compile_, _$rootScope_, _$log_) {
$compile = _$compile_;
$scope = _$rootScope_.$new();
$log = _$log_;
spyOn($log, 'log');
});
});
it('should work', function() {
var el = $compile('<my-directive></my-directive>')($scope);
console.log('Isolated scope:', el.isolateScope());
el.isolateScope().save();
expect($log.log).toHaveBeenCalled();
});
});
But when I print out the isolated scope, it results in undefined. What really confuses me though, if instead of the templateUrl I simply use template in my directive, then everything works: isolateScope() has a completely scope object as its return value and everything is great. Yet, somehow, when using templateUrl it breaks. Is this a bug in ng-mocks or in the Karma preprocessor?
Thanks in advance.
I had the same problem. It seems that when calling $compile(element)($scope) in conjunction with using a templateUrl, the digest cycle isn't automatically started. So, you need to set it off manually:
it('should work', function() {
var el = $compile('<my-directive></my-directive>')($scope);
$scope.$digest(); // Ensure changes are propagated
console.log('Isolated scope:', el.isolateScope());
el.isolateScope().save();
expect($log.log).toHaveBeenCalled();
});
I'm not sure why the $compile function doesn't do this for you, but it must be some idiosyncracy with the way that templateUrl works, as you don't need to make the call to $scope.$digest() if you use an inline template.
With Angularjs 1.3, if you disable debugInfoEnabled in the app config:
$compileProvider.debugInfoEnabled(false);
isolateScope() returns undefined also!
I had to mock and flush the $httpBackend before isolateScope() became defined. Note that $scope.$digest() made no difference.
Directive:
app.directive('deliverableList', function () {
return {
templateUrl: 'app/directives/deliverable-list-directive.tpl.html',
controller: 'deliverableListDirectiveController',
restrict = 'E',
scope = {
deliverables: '=',
label: '#'
}
}
})
test:
it('should be defined', inject(function ($rootScope, $compile, $httpBackend) {
var scope = $rootScope.$new();
$httpBackend.expectGET('app/directives/deliverable-list-directive.tpl.html').respond();
var $element = $compile('<deliverable-list label="test" deliverables="[{id: 123}]"></deliverable-list>')(scope);
$httpBackend.flush();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
expect($element).toBeDefined();
expect($element.controller).toBeDefined();
scope = $element.isolateScope();
expect(scope).toBeDefined();
expect(scope.label).toEqual('test');
expect(scope.deliverables instanceof Array).toEqual(true);
expect(scope.deliverables.length).toEqual(1);
expect(scope.deliverables[0]).toEqual({id: 123});
}));
I'm using Angular 1.3.
You could configure karma-ng-html2js-preprocessor plugin. It will convert the HTML templates into a javascript string and put it into Angular's $templateCache service.
After set a moduleName in the configuration you can declare the module in your tests and then all your production templates will available without need to mock them with $httpBackend everywhere.
beforeEach(module('partials'));
You can find how to setup the plugin here: http://untangled.io/how-to-unit-test-a-directive-with-templateurl/
In my case, I kept running into this in cases where I was trying to isolate a scope on a directive with no isolate scope property.
function testDirective() {
return {
restrict:'EA',
template:'<span>{{ message }}</span>'
scope:{} // <-- Removing this made an obvious difference
};
}
function testWithoutIsolateScopeDirective() {
return {
restrict:'EA',
template:'<span>{{ message }}</span>'
};
}
describe('tests pass', function(){
var compiledElement, isolatedScope, $scope;
beforeEach(module('test'));
beforeEach(inject(function ($compile, $rootScope){
$scope = $rootScope.$new();
compiledElement = $compile(angular.element('<div test-directive></div>'))($scope);
isolatedScope = compiledElement.isolateScope();
}));
it('element should compile', function () {
expect(compiledElement).toBeDefined();
});
it('scope should isolate', function () {
expect(isolatedScope).toBeDefined();
});
});
describe('last test fails', function(){
var compiledElement, isolatedScope, $scope;
beforeEach(module('test'));
beforeEach(inject(function ($compile, $rootScope){
$scope = $rootScope.$new();
compiledElement = $compile(angular.element('<div test-without-isolate-scope-directive></div>'))($scope);
isolatedScope = compiledElement.isolateScope();
}));
it('element should compile', function () {
expect(compiledElement).toBeDefined();
});
it('scope should isolate', function () {
expect(isolatedScope).toBeDefined();
});
});

Resources