How to trigger event in directive for testing it? - angularjs

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()?

Related

unit test case for handler in ng-change in file input angularjs

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

karma testing failing in directive

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

angular js Unit test for directive jasmine

I have written angular js directive one method, but I don't know how to write unit test for that.
var app = angular.module("myApp",[]);
app.directive('minMax', function() {
return {
require: 'ngModel',
link: function(scope, element, attr, mCtrl) {
function myValidation(value) {
if (value.toString().length > 2 & value.toString().length < 6) {
mCtrl.$setValidity('charE', true);
} else {
mCtrl.$setValidity('charE', false);
}
return value;
}
mCtrl.$parsers.push(myValidation);
}
};
});
How do I test this method?
Have a look here: https://github.com/daniellmb/angular-test-patterns.
It contains a great collection of test patterns.
Example of directive test:
describe('Directive: myDir', function () {
var element, scope, compile, defaultData,
validTemplate = '<my-dir ng-model="data"></my-dir>';
function createDirective(data, template) {
var elm;
// Setup scope state
scope.data = data || defaultData;
// Create directive
elm = compile(template || validTemplate)(scope);
// Trigger watchers
//scope.$apply();
// Return
return elm;
}
beforeEach(function () {
// Load the directive's module
module('myApp');
// Reset data each time
defaultData = 42;
// Provide any mocks needed
module(function ($provide) {
//$provide.value('Name', new MockName());
});
// Inject in angular constructs otherwise,
// you would need to inject these into each test
inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
compile = $compile;
});
});
describe('when created', function () {
// Add specs
});
describe('when the model changes', function () {
// Add specs
});
describe('when destroyed', function () {
// Add specs
});
});

element.click in directive unit test not assigning value to variables

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.

AngularJS changing controller mocks

I'm using this construct:
Directive with a ControllerAs.
The Controller has a depencency on a Service which does REST requests.
The directive and the controller:
angular.module('app')
.directive('thingsList', function () {
return {
templateUrl: 'thingsListEntry-template.html',
restrict: 'A',
controller: 'thingsListController as ctrl'
};
})
.controller('thingsListController', function (thingsStorage) {
thingsStorage.getList().then(angular.bind(this, function (response) {
this.things = response;
}));
});
What I want to do now is to test the directive with a controller mock:
describe('things list test suite', function() {
describe('tests for the directive', function () {
var scope, render, element, mockController;
/**
* Mock the controller
*/
beforeEach(module('app', function ($provide, $controllerProvider) {
$controllerProvider.register('thingsListController', function () {
this.things = [];
mockController = this;
});
}));
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope.$new();
var angularElement = angular.element('<div things-list></div>');
var compileFunction = $compile(angularElement);
render = function () {
element = compileFunction(scope);
$rootScope.$digest();
};
}));
it('should be empty without things', function() {
render();
expect(element[0].querySelectorAll('div.things-list-entry').length).toEqual(0);
});
What I would like to do next is to change the things in the controller mock and test that. I don't know how to do that
it('should contain 1 entry with 1 thing', function () {
mockController.things = [{'name':'1'}];
render();
expect(element[0].querySelectorAll('div.thing-list-entry').length).toEqual(1);
});
Here I'm setting mockController.things, but I'm not sure how to get to the mockController. The version above sets it in the mock setup. I also tried using scope.ctrl.things and couple other things but nothing works. Any suggestions?
Try scope.mockController.things instead of mockController.things.

Resources