Unit test $ formatters in Angular directive
I have a simple validator that checks the input against a regular expression:
.directive('test', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function ($scope, element, attrs, ctrl) {
ctrl.$setValidity('namePattern', true);
function checkValid(name) {
console.log('checkValid executed');
if (!name || ctrl.$pristine) {
ctrl.$setValidity('namePattern', true);
return name;
}
var test = /^[A-Za-z0-9_-]+$/;
ctrl.$setValidity('namePattern', test.test(name));
return name;
}
// called when value changes via code/controller
ctrl.$formatters.unshift(checkValid);
// called when value changes in input element
ctrl.$parsers.unshift(checkValid);
}
};
});
I want to unit test this directive and have this:
function initState() {
angular.mock.module('app');
angular.mock.inject(function($compile, $rootScope, $timeout){
$scope = $rootScope.$new();
$rootScope.safeApply = function(cb) {
$timeout(function() {
$scope.$digest();
});
};
$scope.model = {
instanceName: ''
};
var tmpl = angular.element('<form name="form">' +
'<input ng-model="model.instanceName" name="instanceName" test>' +
'</form>'
);
element = $compile(tmpl)($scope);
$scope.$apply();
form = $scope.form;
});
}
beforeEach(initState);
However, changes to the model do not trigger checkValid
. I tried to directly set the property on the model:
it('should trigger a change resulting in an invalid state', function () {
$scope.model.instanceName = 'this is an invalid name';
$scope.$digest();
expect(form.instanceName.$valid).to.be.false;
});
as well as neutralization using $modelValue
:
it('should trigger a change resulting in an invalid state', function () {
form.instanceName.$modelValue = 'this is an invalid name';
$scope.$digest();
expect(form.instanceName.$valid).to.be.false;
});
I also tried to fire the event input
via element.triggerHandler
.
How can I force the model change to be checkValid
executed through $formatters
?
(Angular 1.2.23 used)
source to share
It looks like your directive states that the input is valid if untouched. In the case of the unit test that you wrote, it will always be pristine as there is no user interaction with it.
If you want the directive to continue as it does now, then in your test test you can force the input to not be pristine: (I'm using Jasmine here)
it('should trigger a change resulting in an invalid state', function () {
$scope.model.instanceName = 'this is an invalid name';
form.instanceName.$pristine = false;
$scope.$digest();
expect(form.instanceName.$valid).toEqual(false);
});
This can be seen at http://plnkr.co/edit/TEd91POjw9odNHSb6CQP?p=preview
Since your directive explicitly assumes that the input is valid for model changes to be input in a pristine state, then in fact I think a more valuable test is to check this explicitly:
it('should ignore invalid values on a pristine input', function () {
$scope.model.instanceName = 'this is an invalid name';
form.instanceName.$setPristine();
$scope.$digest();
expect(form.instanceName.$valid).toEqual(true);
});
source to share