Mocking formController in directive - angularjs

I have a directive that checks for the validity of the form before doing something. It's working, but my unit tests keep on failing because I can't mock the formController in my karma tests.
I have the following directive:
.directive('directiveName', function() {
return {
restrict: 'A',
require: 'form',
link: function(scope, element, attrs, ctrl) {
element.bind('submit', function(event) {
if(!ctrl.$valid) ...do something here...
}
}
};
});
This is my test initialization:
var form, element, $scope;
beforeEach(inject(function($rootScope, $compile, $q) {
$scope = $rootScope.$new();
var template = angular.element('<form fl-submit="mockFunction()">' +
'<button type="submit"> </button> </form>');
$scope.mockFunction = function() {
return promise.promise;
};
element = $compile(template)($scope);
form = $scope.form;
}));
describe('and the form is not valid', function() {
form.$valid = false;
...expect something here...
});
Thanks very much for the help!

Related

Angularjs - Test unit directive only numbers

I have a directives.js file with two directives and I would like apply unit testing necesary, to improve coverage, but I do not know how to do it
This the directive.js file:
(function () {
'use strict';
angular
.module('app')
.directive('numbersOnly', function(){
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
var transformedInput = inputValue ? inputValue.replace(/[^\d.-]/g,'') : null;
if (transformedInput!=inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
}
})
.directive('numbersOnlyPositive', function(){
return {
require: 'ngModel',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
var transformedInput = inputValue ? inputValue.replace(/[^\d.]/g,'') : null;
if (transformedInput!=inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
}
});
})();
This is the tests file (directives.spec.js):
(function () {
'use strict';
describe('Unit testing directives general', function() {
var $compile,
$rootScope,
element,
vm;
// Load the app module, which contains the directive
beforeEach(module('app'));
// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
beforeEach(inject(function(_$compile_, _$rootScope_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$compile = _$compile_;
$rootScope = _$rootScope_;
vm.mesHastaSelected = "3";
vm.cantidadHastaSelected = "5";
//element = angular.element('<input text ng-model="mesHastaSelected">');
}));
it('should accept number input only', function() {
expect(vm.cantidadHastaSelected).toBe('1');
});
it('should accept number positive input only', function() {
expect(vm.mesHastaSelected).toBe('8');
});
});
})();
This is the html code input fields:
<input type="text" ng-model="vm.mesHastaSelected" maxlength="2" numbers-only-positive />
<input type="text" ng-model="vm.cantidadHastaSelected" maxlength="4" numbers-only />
Thanks for the help,

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.

Unit testing Angular directive with $http

I have an Angular directive that, when attached to an <input>, waits one second after user input before querying an endpoint with $http. In short, it's meant to check username uniqueness.
It looks like this:
.directive('sgValidUsername', ['$http', function(http) {
var waitTimer;
var checkIfUserExists = function(e, ctrl) {
http.get('/publicapi/users/' + e.target.value + '/exists')
.success(function(data) {
ctrl.$setValidity('unique', !data.exists);
});
};
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
element.on('blur keyup', function(e) {
if (e.target.value) {
clearInterval(waitTimer);
waitTimer = setTimeout(function() {
checkIfUserExists(e, ctrl);
}, 1000);
}
});
}
};
}]);
I'm trying my best to write a good comprehensive Jasmine unit test suite, but it's not working out because I couldn't find an appropriate example to learn from. I end up reconstructing the directive in test form rather than actually testing the directive. Also, I get a 'no pending request to flush' error.
Any suggestions for the below?
describe('SignupForm directives', function () {
var form, // el to which directive is applied
$scope,
$httpBackend, // $http mock
usernameExistsHandler;
beforeEach(function() {
module('signupform.directives');
});
beforeEach(function() {
inject(function ($injector, $rootScope, $compile, $q, $timeout) {
$scope = $rootScope;
$httpBackend = $injector.get('$httpBackend');
usernameExistsHandler = $httpBackend.whenGET(/\/publicapi\/users\/.+?\/exists/);
var el = angular.element('<form name="form"><input type="text" name="username" ng-model="user.username" sg-username-is-valid /></form>');
$scope.user = { username: null };
$compile(el)($scope);
form = $scope.form;
});
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should invalidate with existing usernames', function() {
form.username.$setViewValue('username_in_use');
$scope.$digest();
expect($scope.user.username).toEqual('username_in_use');
usernameExistsHandler.respond('200', { exists: true });
$httpBackend.expectGET('/publicapi/users/' + $scope.user.username + '/exists/');
$httpBackend.flush();
expect(form.username.$valid).toBe(false);
});

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>

Getting value from ngModel in directive when using object with property

Not sure how to phrase the question so please edit if you can come up with something better. I have the following directive:
app.directive('foo', function() {
return {
restrict: 'A',
require: "?ngModel",
link: function (scope, element, attrs, controller) {
scope.$watch(attrs.ngModel, function () {
console.log("Changed to " + scope[attrs.ngModel]);
});
}
};
});
When I have this it works great and logs properly
<input type="text" ng-model="bar" />
app.controller('fooController', function($scope) {
$scope.bar = 'ice cream';
});
It doesn't work when I try it this way around. It keeps logging 'Changed to undefined'
<input type="text" ng-model="model.bar" />
app.controller('fooController', function($scope) {
$scope.model = { bar: 'ice cream' };
});
How do I make it work for both scenarios. It seems the right thing to do seeing as angular lets you use both.
I looked at ngModel directive and found a function called ngModelGet. Uses $parse.
app.directive('foo', function($parse) {
return {
restrict: 'A',
require: "?ngModel",
link: function (scope, element, attrs, controller) {
var ngModelGet = $parse(attrs.ngModel);
scope.$watch(attrs.ngModel, function () {
console.log("Changed to " + ngModelGet(scope));
});
}
};
});
your can use
var ngModelCtrl = controller;
ngModelCtrl.$viewValue
replace
scope[attrs.ngModel]
here is ngModelCtrl sdk

Resources