I have the following directive:
!(function (window, angular) {
'use strict';
/**
* @ngdoc directive
* @name app.directive:social
* @description
* # social
*/
angular.module('app')
.directive('social', function(social_network_conf) {
return {
restrict: 'A',
scope: {
social: "@"
},
require: 'ngModel',
controller: function($scope, $element){
//for tests only
$scope.render = function(){
//how to I get the ngModel here
ngModel.$render();
};
$scope.setViewValue = function(val){
ngModel.$setViewValue(val);
};
},
link: function(scope, element, attr, ngModel) {
ngModel.$formatters.push(function(value) {// from model to view
value = value.trim();
if(value){
if (value.indexOf(social_network_conf.matcher) === 0){
var split_link = value.split(social_network_conf.divider);
return split_link[split_link.length-1];
}
else{
return value;
}
}
});
ngModel.$parsers.push(function(value) { // from view to model
value = value.trim();
if(value){
if (value.indexOf(social_network_conf.matcher) === 0){
return value;
}
else{
return social_network_conf.prefix + scope.social +
social_network_conf.suffix + value;
}
}
});
}
};
});
}(window, window.angular));
The test goes as following:
'use strict';
describe('Directive: social', function () {
// load the directive's module
beforeEach(module('app'));
var element,
social_network_conf,
linker,
scope,
$httpBackend;
beforeEach(inject(function ($rootScope, _$httpBackend_, _social_network_conf_) {
scope = $rootScope.$new();
social_network_conf = _social_network_conf_;
//Must be an object to make use of prototypical inheritence for out-side-of-isolate-scope access
scope.models = {};
$httpBackend = _$httpBackend_;
$httpBackend.whenGET(/views\/social.html/).respond('<div></div>');
$httpBackend.whenGET(/views\/navigation.html/).respond('<div></div>');
}));
it('It should convert ngModel into full HTTP address notation', inject(function ($compile) {
element = angular.element('<input social="test_network" ng-model="models.test_network"></social>');
linker = $compile(element);
element = linker(scope);
scope.$apply(function(){
element.val('test');
});
scope.$digest();
expect(scope.models.test_network).toBe(social_network_conf.prefix + 'test' +
social_network_conf.suffix);
// expect(element.text()).toBe('this is the social directive');
}));
});
Problem is that those lines:
scope.$apply(function(){
element.val('test');
});
Don't actually invoke the $parser I defined.
Though of creating a controller for the directive with an API to call ngModel.$render
or ngModel.$setViewValue
but I have no way to access the ngModel at the directive controller without an ugly hack.
Unit Test in Jasmine You use TestComponentBuilder as shown in the lined SO question/answer. Create a test component where the directive is used in the template and then get a reference to the directive from the created test component instance.
Karma. Karma is a test runner for JavaScript. Along with Jasmine, Karma is one of the default testing tools for Angular.
To run the test, you will only need to run the command ng test . This command will also open Chrome and run the test in watch mode, which means your test will get automatically compiled whenever you save your file. In your Angular project, you create a component with the command ng generate component doctor .
2 possible solutions:
First is to wrap the element inside a form, and to assign the input field and the form a name
attribute, and then access the input field as following:
scope.form_name.input_name.$setViewValue('value')
Working code:
'use strict';
describe('Directive: social', function () {
// load the directive's module
beforeEach(module('app'));
var element,
social_network_conf,
linker,
scope,
$compile,
$body,
html,
$httpBackend;
beforeEach(inject(function ($rootScope, _$compile_, _$httpBackend_, _social_network_conf_) {
scope = $rootScope.$new();
social_network_conf = _social_network_conf_;
//Must be an object to make use of prototypical inheritence for out-side-of-isolate-scope access
scope.models = {};
$compile = _$compile_;
$httpBackend = _$httpBackend_;
$body = $('body');
$httpBackend.whenGET(/views\/social.html/).respond('<div></div>');
$httpBackend.whenGET(/views\/navigation.html/).respond('<div></div>');
$body.empty();
html = '<form name="testForm">' +
'<input social="test_network" name="test" ng-model="models.test_network">' +
'</form>';
}));
it('It should convert ngModel into full HTTP address notation', function () {
element = angular.element(html);
linker = $compile(element);
element = linker(scope);
var viewValue = 'test',
input = element.find('input');
scope.models.test_network = viewValue;
scope.$digest();
scope.testForm.test.$setViewValue(viewValue);
scope.$digest();
expect(scope.models.test_network).toBe(social_network_conf.prefix + input.isolateScope().social +
social_network_conf.suffix + viewValue);
});
Alternatively, the second one offers to dispatch an input event on the element - didn't work for me with Angular 1.3. This is demonstrated in This YouTube video.
element.val('test');
element.trigger('input');
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With