Unit Test Directive with Isolated Scope - angularjs

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" />

Related

check attribute from html - angular directive testing jasmine karma

how to check attribute is present in HTML and match its value. this is a test spec.js I wrote,
define(['angular',
'angularMocks',
'site-config',
'ng_detector',
],
function(angular,
mock,
$app,
ng_detector) {
describe('ng-detector controller', function() {
beforeEach(angular.mock.module("webapp"));
var $compile, $rootScope, tpl, $scope, elm, templateAsHtml;
beforeEach(angular.mock.inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
// $scope = _$rootScope_.$new();
}));
it('should initialize the ng-detector directive', inject(function() {
var tpl = $compile("<div ng-detector ></div>")($rootScope);
$rootScope.$digest();
console.log(tpl) // Log: r{0: <div ng-detector="" class="ng-scope" ng-verison="1.6.4"></div>, length: 1}
templateAsHtml = tpl[0].outerHTML;
expect(templateAsHtml.attr('ng-version')).toEqual(angular.version.full);
}));
});
});
directive. that adds angular version to attribute ng-version
'use strict';
define(['app-module'], function(ng) {
$app.info('ng detector initialized. {file: directives/ng-detector.js}');
ng.directive('ngDetector', function() {
return {
restrict: "A",
link: function(scope, elm, attr) {
elm.attr('ng-version', angular.version.full);
}
};
});
return ng;
});
I want to get a ng-version attribute set by the directive and match the attribute value.
I figured out myself. I was looking at the different place.
it('should check the angular version number', angular.mock.inject(function() {
expect(tpl.attr('ng-version')).toEqual(angular.version.full);
}));

unit testing directive with link using controller

I'm trying to unit test my directive that set form validity depending on a controller variable.
My directive code :
angular.module('myModule',[])
.directive('myDirective', function() {
return {
restrict: 'A',
link: function(scope, element, attr, ctrl) {
scope.$watch("mailExist", function(){
if(scope.mailExist) {
ctrl.$setValidity('existingMailValidator', false);
} else {
ctrl.$setValidity('existingMailValidator', true);
}
});
}
};
});
When trying to unit test this directive, I'm trying to isolate the controller ctrl with this code:
describe('directive module unit test implementation', function() {
var $scope,
ctrl,
form;
beforeEach(module('myModule'));
beforeEach(inject(function($compile, $rootScope) {
$scope = $rootScope;
var element =angular.element(
'<form name="testform">' +
'<input name="testinput" user-mail-check>' +
'</form>'
);
var ctrl = element.controller('userMailCheck');
$compile(element)($scope);
$scope.$digest();
form = $scope.testform;
}));
describe('userMailCheck directive test', function() {
it('should test initial state', function() {
expect(form.testinput.$valid).toBe(true);
});
});
});
Running this test, I still obtain:
Cannot read property '$setValidity' of undefined
that's mean I haven't really inject a controller.
What is wrong in my test?
Finally in found the solution:
first in code I have add :
require: 'ngModel',
and then modified the unit test as follow:
describe('directive module unit test implementation', function() {
var scope,
ngModel,
form;
beforeEach(module('myModule'));
beforeEach(inject(function($compile, $rootScope) {
scope = $rootScope.$new();
var element =angular.element(
'<form name="testform">' +
'<input name="testinput" ng-model="model" user-mail-check>' +
'</form>'
);
var input = $compile(element)(scope);
ngModel = input.controller('ngModel');
scope.$digest();
form = scope.testform;
}));
describe('userMailCheck directive test', function() {
it('should test initial state', function() {
expect(form.testinput.$valid).toBe(true);
});
});
});
and everything works fined.

Test a simple function in a directive, angular

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.

Test Pre-Compile function on directive

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

Unit-testing a directive with isolated scope, bidirectional value and ngIf

I want to unit test a directive which looks like this:
angular.module('myApp', [])
.directive('myTest', function () {
return {
restrict: 'E',
scope: { message: '='},
replace: true,
template: '<div ng-if="message"><p>{{message}}</p></div>',
link: function (scope, element, attrs) {
}
};
});
Here is my failing test:
describe('myTest directive:', function () {
var scope, compile, validHTML;
validHTML = '<my-test message="message"></my-test>';
beforeEach(module('myApp'));
beforeEach(inject(function($compile, $rootScope){
scope = $rootScope.$new();
compile = $compile;
}));
function create() {
var elem, compiledElem;
elem = angular.element(validHTML);
compiledElem = compile(elem)(scope);
scope.$digest();
return compiledElem;
}
it('should have a scope on root element', function () {
scope.message = 'not empty';
var el = create();
console.log(el.text());
expect(el.text()).toBeDefined();
expect(el.text()).not.toBe('');
});
});
Can you spot why it's failing?
The corresponding jsFiddle
Thanks :)
console.log((new XMLSerializer()).serializeToString(el.get(0)));
returns
<!-- ngIf: message -->
because you are using replace without a parent element in validHTML with combinaison of ng-if .So either you change validHTML and add a div as parent.
or
test your expectations on the next sibling of el
el.next()
which will be
<div ng-if="message" message="message" class="ng-scope"><p class="ng-binding">yes</p></div>

Resources