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();
});
});
Related
I used a directive to add a listener function on input file change, I'm going to create a unit test case for this in jasmine, but I'm not able to trigger function in unit case.
unit test code:
describe('customOnChange', function () {
beforeEach(module('app'));
beforeEach(inject(function ($rootScope, $compile, $parse) {
this.$rootScope = $rootScope;
this.compile = $compile;
this.parse = $parse;
this.initDirective = function (config) {
this.scope = this.$rootScope.$new();
this.scope.listnerFunction = function($event){
console.log("event called : ",$event);
};
this.element = angular.element('<input type="text" name="textField" id="textField" dynamic-data-editor-custom-on-change="listnerFunction($event)" ng-model="testText"/>');
this.compile(this.element)(this.scope);
this.$rootScope.$digest();
$('body').append(this.element);
this.$rootScope.$digest();
};
}));
fit('Applied directive successfully', function() {
this.initDirective();
spyOn(this.scope,'listnerFunction');
var ele = this.element.find("input#textField").eq(0);
console.log('element', ele);
ele.triggerHandler('change');
this.$rootScope.$apply();
expect(this.scope.listnerFunction).toHaveBeenCalled();
});
});
The main directive code:
function () {
'use strict';
angular.module('app')
.directive('customOnChange', function ($parse) {
return {
restrict: 'A',
link: function ($scope, element, attrs, ngModel) {
var attrHandler = $parse(attrs.dynamicDataEditorCustomOnChange);
var handler = function (e) {
$scope.$apply(function () {
attrHandler($scope, { $event: e });
});
};
element[0].addEventListener('change', handler, false);
}
};
});
})();
I got the error on running test
Expected spy listnerFunction to have been called.
Please help me to resolve the test case for the directive.
In your test the problem is: spy is created after the method listnerFunction function is called, so you see the error.
You should replace:
this.scope.listnerFunction = function($event){
console.log("event called : ",$event);
};
with (andCallFake for jasmine < 2)
this.scope.listnerFunction =
jasmine.createSpy("listnerFunction")
.and.callfake(function($event){
console.log("event called : ",$event);
});
I've simple directive with an event handler.
angular.module('app')
.directive('acc', function () {
return {
restrict: 'A',
link: function (scope, element) {
scope.a = 0;
scope.func = function() {
console.log('h2.clicked !!');
scope.a = 1;
};
element.on('click', 'h2', scope.func);
}
};
});
And simple test
describe('Directive: acc', function () {
beforeEach(module('app'));
var element,
scope;
var clickContext = 'h2';
var onSpy;
beforeEach(inject(function ($compile, $rootScope) {
scope = $rootScope.$new();
onSpy = spyOn($.fn, 'on').and.callThrough();
element = angular.element('<nav acc><h2></h2></nav>');
element = $compile(element)(scope);
scope.$digest();
}));
it('should set click handler', function () {
expect(onSpy).toHaveBeenCalledWith('click', clickContext, jasmine.any(Function));
});
describe('click handler behaviour', function () {
beforeEach(function () {
element.find('h2').triggerHandler('click');
});
it('handler should be called', function () {
expect(scope.a).toBe(1);
});
});
});
I want to invoke handler before each test case for verify behaviour.
How to do it?
I use jasmine for unit testing.
Have you tried calling scope.$digest(); after the .click()?
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()
I am testing the directive below that uses an isolated scope. I know the triggerHandler is working but for some reason I keep getting the error
Expected undefined to equal 'http://www.gravatar.com/avatar/12345?s=40&d=identicon'.
Directive:
angular.module('pb.webSites.directives')
.directive('pbOrganizationImagePicker', [ function () {
return {
restrict: "E",
template: '<img data-ng-src="{{ imageSource }}" width="{{width}}" height="{{height}}" alt="Image Picker" class="img-rounded" />',
scope: {
fileId: '=pbFileId',
defaultSrc: '#pbDefaultSrc',
width: '#pbWidth',
height: '#pbHeight'
},
controller: 'pbOrganizationImagePickerController',
link: function (scope, element, attrs) {
scope.$watch('defaultSrc', function (value) {
if (value !== undefined) {
scope.imageSource = value;
}
});
element.on('click', function () {
scope.pickImage().then(function (image) {
scope.imageSource = image.storageUrl;
scope.fileId = image.fileId;
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
});
}
};
}]);
Tests:
describe('pbOrganizationImagePicker', function () {
beforeEach(module('pb.webSites.controllers'));
beforeEach(module('pb.webSites.directives'));
beforeEach(module('ui.router'));
beforeEach(module('ui.bootstrap'));
var compile;
var scope;
var mockModal = {};
var image;
beforeEach(inject(function ($compile, $rootScope) {
compile = $compile
scope = $rootScope.$new();
}));
beforeEach(inject(function ($q, $injector) {
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET('/app/webSites/directives/OrganizationImagePicker.html').respond(200, '');
scopeObject = {
profileImageUrl: 'http://www.gravatar.com/avatar/12345?s=40&d=identicon',
profileImageId: 54634
};
scope.webSite = {
profileImageId: 6436
};
scope.pickImage = function () {
var defer = $q.defer();
defer.resolve(scopeObject);
return defer.promise;
};
}));
describe('element.click()', function () {
beforeEach(function () {
var html = angular.element('<pb-organization-image-picker data-pb-default-src="{{ webSite.profileImageUrl || \'/content/img/placeholder-lg.jpg\' }}" data-pb-file-id="webSite.profileImageId" data-pb-width="200"></pb-organization-image-picker>');
element = compile(html)(scope);
element.triggerHandler('click');
});
it('should assign value to scope variables', function () {
scope.pickImage();
scope.$digest();
expect(scope.imageSource).toEqual(scopeObject.profileImageUrl);
expect(scope.fileId).toEqual(scopeObject.profileImageId);
});
});
});
I have also tried changing the test to the following since Im pretty sure in the test above I am faking the test a bit. However here I get pickImage() was never called. Even if you dont see the problem which method do you think is better for testing?
describe('element.click()', function () {
it('should assign value to scope variables', function () {
element = compile(html)(scope);
spyOn(scope, 'pickImage');
element.triggerHandler('click');
scope.$apply();
//scope.pickImage();
expect(scope.pickImage).toHaveBeenCalled();
scope.$digest();
expect(scope.imageSource).toEqual(scopeObject.profileImageUrl);
expect(scope.fileId).toEqual(scopeObject.profileImageId);
});
});
element.on('click', function () {
scope.$apply(function() {
scope.pickImage().then(function (image) {
scope.imageSource = image.storageUrl;
scope.fileId = image.fileId;
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
});
});
Wrap the code in your click handler in an $apply.
I suspect the real problem is that your directive uses an isolate scope, so it actually won't have a "pickImage()" method and when you assign imageSource and fileId, you are putting them on the directive scope, and not the scope that your test is trying to validate (the parent scope).
Your test assigns pickImage() and webSite to the scope of your test element. Since you use an isolated scope, your directive won't have access to these methods and properties. You should probably move these to a service and inject them into the directive.
It's not "correct", but to test the theory you can change your directive to:
element.on('click', function () {
scope.$apply(function(){
scope.$parent.pickImage().then(function (image) {
scope.$parent.imageSource = image.storageUrl;
scope.$parent.fileId = image.fileId;
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
});
});
This IS NOT something you want in production code, but I'm just trying to demonstrate how the different scopes are related to each other.
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);
}
}
}