How do I unit test a precompile function on a directive? I can just check the scope post compile but was wondering if there was a better way.
angular.module('app')
.directive('mailcheck', function () {
return {
compile: function() {
return {
pre: function(scope, element, attributes, controller) {
scope.mailcheck = controller;
}
post: ...
}
}
};
});
Test:
'use strict';
describe('Directive: mailcheck', function () {
// load the directive's module
beforeEach(module('8SecondsToTradeGuiApp'));
var element,
scope;
beforeEach(inject(function ($rootScope) {
scope = $rootScope.$new();
element = '<input mailcheck>';
element = $compile(element)(scope);
scope.$digest();
}));
});
Related
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.
I have a custom directive that uses a service. When I run my unit tests, I keep getting the thrown error 'Library does not exist on window'. How can I avoid getting that error in my Unit test?
example service
angular.module('example')
.factory('thirdParty', ['$window', function($window) {
if (typeof $window.thirdParty === 'undefined') {
throw new Error('Library does not exist on window');
} else {
return $window.thirdParty;
}
}]);
custom directive
angular.module('example')
.directive('customDirective', ['thirdParty',
function(thirdParty) {
var defaults, link;
link = function(scope, element, attrs, ctrls) {
// do something with thirdParty
};
return {
restrict: 'A',
require: 'ngModel',
link: link,
};
}]);
test
describe('customDirective', function() {
var element, compile, scope;
beforeEach(module('example'));
beforeEach(inject(function($compile, $rootScope) {
compile = $compile;
scope = $rootScope.$new();
}));
// Manually compile and link our directive
function getCompiledElement(template) {
var compiledElement;
var validTemplate = '<input ng-model="example.data" custom-directive />';
compiledElement = compile(template || validTemplate)(scope);
scope.$digest();
return compiledElement;
}
it('should do something', function() {
element = getCompiledElement();
// expects
});
});
You need to inject $window (stub that out too, for good measure) and stub/mock out the thirdParty lib.
var $window;
beforeEach(function () {
$window = {};
module('yourmodule', function ($provide) {
$provide.value('$window', $window);
});
$window.thirdParty = {};
});
it('throws', function () {
delete $window.thirdParty;
function fn () {
getCompiledElement();
}
expect(fn).to.throw(/does not exist on window/i);
});
it('just works™', function () {
function fn () {
getCompiledElement();
}
expect(fn).to.not.throw;
});
This is my directive:
angular.module('clientApp')
.directive('positionDropDowns', function (CommonFactory) {
return {
templateUrl: 'template/position-drop-downs/position-drop-downs.html',
restrict: 'E',
scope: {
districtsWithSubObjects: '='
},
link: function postLink(scope, element, attrs) {
scope.hello = function(name){
return 'hello ' + name;
}
}
};
});
How do I test the hello function? I tried this:
describe('Directive: positionsDropDowns', function () {
// load the directive's module
beforeEach(module('clientApp'));
beforeEach(module('template/position-drop-downs/position-drop-downs.html'));
var element,
scope;
beforeEach(inject(function ($rootScope) {
scope = $rootScope.$new();
element = angular.element('<position-drop-downs></position-drop-downs>');
$rootScope.$digest();
}));
it('fn hello', inject(function ($compile) {
expect(element.scope.hello('john')).toBe("hello john");
}));
});
I get TypeError: undefined is not a function
You need to compile your custom directive first:
beforeEach(inject(function ($rootScope) {
scope = $rootScope.$new();
element = $compile('<position-drop-downs></position-drop-downs>')(scope);
}));
After that the scope object should be populated with hello method:
it('fn hello', inject(function ($compile) {
expect(scope.$$childTail.hello('john')).toBe("hello john");
}));
URD. zeroflagL in comments gives more elegant way to access isolated directive scope. You can also do
expect(element.isolateScope().hello('john')).toBe("hello john");
Note, that you need to access isolated directive scope. You can do it with $$childTail reference.
I want to add spyOn in a function defined inside the link function of a directive. I tried spyOn(element, function_name) after compiling the element, but it doesn't work.
Expose the function on the directive's scope.
function link (scope, element, attrs) {
// this can be spied on
scope.myFunction = function () {
};
// this won't be able to be spied on
function myPrivateFunction () {
}
}
Retrieve the directive's scope.
var directiveScope = angular.element('#myElement').scope();
// or .isolateScope() for isolate scope directives
Spy on the function.
spyOn(directiveScope, 'myFunction');
You can achieve this by using an isolateScope
//--directive.js--//
(function(angular) {
angular.module('module')
.directive('directive', directive);
directive.$inject = ['dv'];
function directive(dv) {
var directive = {
link: link,
replace: true,
restrict: "E",
scope: {
bindto: '=bindto'
}
};
return directive;
function link(scope, element, attrs) {
scope.generate = generate;
scope.generate();
function generate() {
/**
* Generate code goes here
**/
}
}
}
})(angular);
//--directive.spec.js--//
describe('Unit Test: Line-chart directive', directiveTest);
function directiveTest() {
var dvFactory;
var $compile;
var $scope;
var element;
var compiledElement;
var $isolateScope;
beforeEach(module('module'));
beforeEach(inject(function(_$compile_, _$rootScope_, _dv_) {
$compile = _$compile_;
$scope = _$rootScope_.$new();
dvFactory = _dv_;
element = '<directive></directive>';
//Compile the directive element
compiledElement = $compile(element)($scope);
//Obtain an isolateScope of the compiled element
$isolateScope = compiledElement.isolateScope();
}));
it('should call and spy on generate', function() {
//spyOn a generate() in $isolateScope
spyOn($isolateScope, 'generate').and.callThrough();
//Call the method
$isolateScope.generate();
//Check it is called
expect($isolateScope.generate).toHaveBeenCalled();
});
}
I want to unit test a directive which looks like this:
'use strict';
angular.module('app')
.directive('phone', function () {
return {
require: 'ngModel',
scope: {
user:'=',
},
link: function(scope, element, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
scope.user.division = null;
return viewValue;
});
}
};
});
Here is my failing test
'use strict';
describe('Directive: phone', function () {
// load the directive's module
beforeEach(module('app'));
var element,
scope;
beforeEach(inject(function ($compile, $rootScope) {
scope = $rootScope.$new();
element = angular.element('<form name="form"><input name="phone" ng-model="user.phone" phone></form>');
scope.user = {};
element = $compile(element)(scope);
element.scope().$digest();
}));
it('should work', function() {
scope.form.phone.$setViewValue('1');
});
});
But when I try to run it I get:
TypeError: 'undefined' is not an object (evaluating 'scope.user.division=null')
Your isolate scope implies that there should be a user attribute on the element that references a user entity on the parent $scope.
In you compiled HTML, though, there is no user attribute, therefore your isolate $scope's user property is undefined.
Your HTML should look like this:
<input ... user="user" />