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);
}
}
}
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 new to unit test.
Please help me to write test case code for following code:
$scope.convertToInt = function (str) {
if (!isNumberEmpty(str) && !isNaN(str)) {
return parseInt(str, 10);
}
return "";
}
I have tried like this, but not able to do.
describe('ConfigurationTestController', function() {
beforeEach(module('usp.configuration'));
describe('ConfigurationController', function () {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('ConfigurationController', {
'$scope': scope
});
}));
});
});
Please tell me how can I write.......
you need to modify your code a bit. you don't need to use describe twice in your test case.
(function() {
"use strict";
describe("test suite for Configuration test controller", function() {
var scope = null;
beforeEach(module("usp.configuration"));
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
$controller("ConfigurationController", {
$scope: scope
});
}));
it("should convert to int", function() {
expect(scope.convertToInt("2")).to.equal(2);
});
it("should return empty string", function() {
expect(scope.convertToInt("asd")).to.equal("");
});
});
}());
Good day!
This is my first time writing a unit test and I don't even know where to begin. I have look through some example and here is my code.
This is a validation function located in my directive
scope.validateInput = function () {
if (scope.description < 100) {
scope.validate_description_message = true;
}
}
Here is my unit test
describe('scope.validateInput', function() {
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope;
element = angular.element(html);
$compile(element)(scope);
scope.$digest();
}));
it('should return true when scope.description is less than 100 characters.', inject(function() {
$rootScope.description = 1;
$rootScope.validateInput();
expect($rootScope.validate_description_message).toEqual(true);
}));
});
Thank you in advance!
You should declare your $scope inside the describe first. And create a new scope from the $rootscope.
describe('scope.validateInput', function() {
var scope;
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
element = angular.element(html);
$compile(element)(scope);
scope.$digest();
}));
it('should return true when scope.description is less than 100 characters.', inject(function() {
scope.description = 1;
scope.validateInput();
expect(scope.validate_description_message).toEqual(true);
}));
});
I have a directive that look like this:
angular.module('myApp', [])
.directive('myDirective', ['$window','MyConfig', function($window,MyConfig){
return {
link: function(scope, element, attr, controller) {
var w = angular.element($window);
function adjustTop(){
var oldtop = scope.top;
if(oldtop<15){
scope.top += 2;
}else{
scope.top = 0;
}
}
if(MyConfig.adjusttop){
w.bind('scroll', function () {
adjustTop();
});
};
}
};
}]);
How can I spyOn to detect adjustTop() function have been called when MyConfig.adjusttop is true?
You shouldn't do that. Testing private members arguably isn't a good idea because it couples your tests to a specific implementation, making them fragile.
In general you should test what your code does, not how it does it. In this case, you could write tests to ensure the scope.top property is correctly updated after the scroll event has been triggered.
Updated to address OP's comment:
Here's a way for you to test what you want:
describe('my test', function() {
var $scope, $window, $compile, MyConfigMock;
beforeEach(function() {
module('plunker');
MyConfigMock = {};
module(function($provide) {
$provide.value('MyConfig', MyConfigMock);
});
inject(function($rootScope, _$window_, _$compile_) {
$scope = $rootScope;
$window = _$window_;
$compile = _$compile_;
});
});
it('updates top on scroll if set to do so', function() {
// Arrange
MyConfigMock.adjusttop = true;
$scope.top = 15;
$compile('<my-directive></my-directive>')($scope);
// Act
$($window).trigger('scroll');
// Assert
expect($scope.top).toBe(0);
});
});
Working Plunker
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()