I want to test the following directive "spinInput" which requires ngModel, but I can't access the directive's scope. All I get is an empty scope. Angularjs 1.3.13 is used.
Directive:
angular.module('charts.spinInput',[]).directive('spinInput', function() {
return {
restrict: 'E',
replace: true,
require:'ngModel',
scope: {
min:"#"
},
controller:function($scope)
{
$scope.test=12;
$scope.calcAngle=function(point)
{
var xDif=point.x-50;
var yDif=point.y-50;
return (Math.atan2(yDif, xDif) * 180 / Math.PI);
};
},
templateUrl: 'charts/directive/spinInput/spinInput.html',
link:function(scope, element, attr,ngModel) {
...
}
};
});
Unit Test:
throws following error: TypeError: 'undefined' is not an object (evaluating 'innerScope.min')
describe('spinInput', function() {
beforeEach(module('charts'));
var $httpBackend;
var element;
var outerScope;
var innerScope;
beforeEach(inject(function($rootScope, $compile , $injector) {
element = angular.element('<spin-input min="12"></spin-input>');
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET('charts/directive/spinInput/spinInput.html').respond(200, '');
outerScope = $rootScope.$new();
$compile(element)(outerScope);
innerScope = element.isolateScope();
outerScope.$digest();
}));
it('scope.min should be defined', function() {
expect(innerScope.min).toBeDefined();
});
});
The way you are constructing your test seems to be causing issues.
I have been able to successfully test the isolated scope as per below.
You can view the test running on this jsfiddle (with templates code commented out).
describe('Directive: spinInput', function() {
var scope, compile, validHTML, templateHtml;;
validHTML = '<spin-input min="12"></spin-input>';
beforeEach(module('myApp'));
beforeEach(inject(function($compile, $rootScope, $templateCache){
templateHtml = $templateCache.get('charts/directive/spinInput/spinInput.html');
if(!templateHtml) {
templateHtml = $.ajax('charts/directive/spinInput/spinInput.html', {async: false}).responseText;
$templateCache.put('charts/directive/spinInput/spinInput.html', templateHtml)
}
scope = $rootScope.$new();
compile = $compile;
}));
function create() {
var elem, compiledElem;
elem = angular.element(validHTML);
compiledElem = compile(elem)(scope);
scope.$digest();
return compiledElem;
}
it('scope.min should be defined', function() {
var el = create();
expect(el.isolateScope().min).toBeDefined();
});
it('scope.min should equal 12', function() {
var el = create();
expect(el.isolateScope().min).toEqual('12');
});
});
Try putting the outerScope.$digest() before element.isolateScope()
Related
I have the following directive. This Directive will trigger after animation end triggers. I now just want to add test for this directive. I have created the test case for this. But it's failing. Can anybody help on this? This is plunker
angular.module('movieApp')
.directive('animationend', function() {
return {
restrict: 'A',
scope: {
animationend: '&'
},
link: function(scope, element) {
var callback = scope.animationend(),
events = 'animationend webkitAnimationEnd MSAnimationEnd' +
'transitionend webkitTransitionEnd';
element.on(events, function(event) {
console.log('[animationend directive] callback');
callback.call(element[0], event);
});
}
};
});
And my test case is
describe('Directive: animationend', function () {
// load the directive's module
beforeEach(module('movieApp'));
var elements,
scope,
compile;
beforeEach(inject(function ($rootScope, _$compile_) {
scope = $rootScope.$new();
compile = _$compile_;
elements = angular.element('<div animationend="" ></div>');
elements = compile(elements)(scope);
scope.$digest();
}));
it('should trigger the callback once animationclass is added ', function () {
scope.ctrl = {
animationend: jasmine.createSpy('animationend')
};
scope.$digest();
var events = 'animationend webkitAnimationEnd MSAnimationEnd' +
'transitionend webkitTransitionEnd';
angular.element(elements).triggerHandler(events);
// element.trigger(events);
expect(scope.ctrl.animationend).toHaveBeenCalled();
});
});
I am trying to write a unit test for my custom directive:
.directive('uiList', [
function(scriptingService) {
return {
scope: {
lengthModel: '=uiList'
},
link: function(scope, elm, attrs) {
scope.$watch('lengthModel', function() {
scriptingService.getScript(request).then(function(scripts) {
//something
});
});
}
};
}
Inside I call a service:
.service('scriptingService', function() {
var scriptingService = {
getScript: function() {
return 'blaat';
}
};
return scriptingService;
})
I would like to test whether the getScript method is called so I wrote this test;
beforeEach(inject(function($rootScope, $compile, _scriptingService_) {
scope = $rootScope.$new();
scope.row = 1;
scriptingService = _scriptingService_;
spyOn(scriptingService, 'getScript');
element = angular.element('<ul id="rows" ui-list="row">');
$compile(element)(scope);
scope.$digest();
elementScope = element.scope();
}));
it('should call service', function() {
scope.$apply(function() {
scope.row = 2;
});
expect(scriptingService.getScript).toHaveBeenCalled();
});
At the moment I get an error:
TypeError: Cannot read property 'getScript' of undefined.
Why do I get this error and how can I fix it? I thought I mocked the service out already?
plunkr: http://plnkr.co/edit/IQOCut?p=preview
Clean version -
http://plnkr.co/edit/8SVlzG?p=preview
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope.$new();
scope.row = 1;
element = angular.element('<ul id="rows" ui-list="row">');
$compile(element)(scope);
scope.$digest();
elementScope = element.scope();
}));
it('should call service', function() {
scope.$apply(function() {
scope.row = 2;
});
expect(elementScope.test).toEqual(1);
});
Below is my directive--
define(['jquery', 'angular'],function($, angular){
var publishToolSummaryDirective = angular.module('sdm.publishTool.directives').directive('krnPublishToolSummary',
function($compile){
return {
restrict: 'EA',
templateUrl: "apps/sdm/pages/partials/publishToolSummary.html",
link : function(scope, element){
var elem =$(element);
scope.previousComment = "";
scope.displayPublishingCommentText = false;
scope.displayCommentBox = function(event){
event.preventDefault();
scope.displayPublishingCommentText = true;
};
scope.displayCommentLink = function(event){
if(event.target.textContent === "cancel"){
scope.publishingCommentText = scope.previousComment;
}else{
scope.previousComment = scope.publishingCommentText;
}
scope.displayPublishingCommentText = false;
}
}
}
});
return publishToolSummaryDirective;
})
Below is my Unit Test--
define([
'apps/sdm/app',
'angular',
'jquery',
'angular-mocks'
], function ($, angular) {
describe("Unit testing Summary directive", function() {
angular.mock.module('sdm.publishTool.directives');
var compile,scope, q,url,elm;
beforeEach(inject(["$templateCache", "$compile","$rootScope", "$q","$httpBackend",function($templateCache, $compile, $rootScope, $q, $httpBackend) {
q = $q;
compile = $compile;
scope = $rootScope.$new();
var html='<krn-publish-tool-summary></krn-publish-tool-summary>';
elm = compile(html)(scope);
scope.$digest();
console.log(elm);
}]));
it("should compile", function(){
expect(elm.find('button').length).toEqual(3);
expect(elm.className).toContain('publishTool-summary');
});
})
})
It is not building my templateURL mentioned in the directive.
console o/p is
Object{0: <krn-publish-tool-summary class="ng-scope"></krn-publish-tool-summary>, length: 1}
Coverage report shows that it is not even going inside the directive 2nd line.
Any help please!
The html variable is not an angular element so it is not giving desired result.
Try changing
var html='<krn-publish-tool-summary></krn-publish-tool-summary>';
to
var html=angular.element('<krn-publish-tool-summary></krn-publish-tool-summary>');
I am trying to unit test a directive that uses ngModel and having difficulties. It seems that the link function of my directive is never being called...
Here is my directive code:
coreModule.directive('coreUnit', ['$timeout', function ($timeout) {
return {
restrict: 'E',
require: '?ngModel',
template: "{{output}}",
link: function (scope, elem, attrs, ngModelCtrl) {
ngModelCtrl.$render = function () {
render(ngModelCtrl.$modelValue);
};
console.log("called");
function render(unit) {
if (unit) {
var output = '(' +
unit.numerator +
(unit.denominator == '' ? '' : '/') +
unit.denominator +
(unit.rate == 'NONE' || unit.rate == '' ? '' : '/' + unit.rate) +
')';
scope.output = output == '()' ? '' : output;
}
}
}
}
}]);
Here is my test spec:
describe('core', function () {
describe('coreUnitDirective', function () {
beforeEach(module('core'));
var scope,
elem;
var tpl = '<core-unit ng-model="myUnit"></core-unit>';
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
scope.myUnit = {};
elem = $compile(tpl)(scope);
scope.$digest();
}));
it('the unit should be empty', function () {
expect(elem.html()).toBe('');
});
it('should show (boe)', function () {
scope.myUnit = {
numerator: 'boe',
denominator: "",
rate: ""
};
scope.$digest();
expect(elem.html()).toContain('(boe)');
});
});
});
The console log output "called" is never occurring and obviously the elem in my test spec is never updating.
What am I doing wrong??
Turns out that I wasn't including the directive in my karma.config file :S. Adding it in resolved all of my issues.
You can try out two things.
First, instead of using just a string tpl, try angular.element().
var tpl = angular.element('<core-unit ng-model="myUnit"></core-unit>');
Second, place the tpl in the beforeEach block. So the result should look like this:
beforeEach(inject(function ($rootScope, $compile) {
var tpl = angular.element('<core-unit ng-model="myUnit"></core-unit>');
scope = $rootScope.$new();
scope.myUnit = {};
elem = $compile(tpl)(scope);
scope.$digest();
}));
How do I check if $emit has been called in an unit test of a directive?
My directive is fairly simple (for the purpose of illustration):
angular.module('plunker', [])
.directive('uiList', [function () {
return {
scope: {
lengthModel: '=uiList',
},
link: function (scope, elm, attrs) {
scope.$watch('lengthModel', function (newVal) {
scope.$emit('yolo');
});
}
}
}])
so every time the attribute uiList changes, it will emit the event.
And I have unit test code as follows:
describe('Testing $emit in directive', function() {
var scope;
var element;
//you need to indicate your module in a test
beforeEach(function () {
module('plunker');
inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
scope.row= 1;
spyOn(scope,'$emit');
element = angular.element('<ul id="rows" ui-list="row">');
$compile(element)(scope);
});
});
it('should emit', function() {
scope.$digest();
scope.row = 2;
scope.$digest();
expect(scope.$emit).toHaveBeenCalledWith("yolo");
});
});
This would always give me error stating that the scope.$emit has never been called.
Is there something wrong with the scope? Can someone please help?
Plunker:http://plnkr.co/edit/AqhPwp?p=preview
Your directive creates an isolated scope, which is calling $emit,so you need to spy on this one ;)
describe('Testing $emit in directive', function() {
var scope;
var element;
var elementScope;
beforeEach(module('plunker'));
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope.$new();
scope.row = 1;
element = angular.element('<ul id="rows" ui-list="row">');
$compile(element)(scope);
scope.$digest();
elementScope = element.scope();
}));
it('should emit', function() {
// arrange
spyOn(elementScope, '$emit');
// act
scope.$apply(function() {
scope.row = 2;
});
// assert
expect(elementScope.$emit).toHaveBeenCalledWith("yolo");
});
});
Fixed plunker here :)
I'm not sure how did it work for glepetre (not sure that it does actually). For me it didn't. The thing is that the directive runs $new on incomming scope so the scope it creates inside and element.scope() are some thing different. You can check their $id fields. So it does not help to spy on the scope you got from element.scope(). I had smilar problem and had to do a little trick to make it work.
Here comes my test :
beforeEach(inject(function () {
scope = $rootScope.$new();
scope.$apply(function () {
scope.field = null;
scope.name = 'nolength';
});
spyOn(scope, '$new').and.returnValue(scope);
spyOn(scope, '$emit');
var element = angular.element('<my-directive name="name" field="field" title="\'Title\'" ></my-directive>');
noLengthValidation = $compile(element)(scope);
scope.$apply();
elementScope = element.scope();
}));
So the trick was to mock $new function on scope so instead of creating a new scope behind the scene and mess the things up it will return itself! Here comes the test itself :
it('The directive should not react on string length if no max-chars attribute presented', function () {
scope.$apply();
noLengthValidation.isolateScope().field = 'fieldisssssssveeeeerryyyyyylooooooonnnngggggggggggggggggggggggggggg';
scope.$apply();
expect(scope.$emit).toHaveBeenCalledWith('ON_MORE_AWASOME_EVENT', 'nolength');
});
And the the part on the directive that calls $emit. Just in case :
function validate() {
scope.validate = 1;
if (scope.field) {
if (!isValid()) {
scope.$emit('ON_AWASOME_EVENT', {code : scope.name, text : 'Field ' + scope.title + ' is feels bad'});
} else {
scope.$emit('ON_MORE_AWASOME_EVENT', scope.name);
}
}
}