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

      

Live example

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)

+3


source to share


2 answers


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

      

+1


source


Use $setViewValue

by specifying the form name and input name.

$scope.form.instanceName.$setViewValue('hello');

      



Then we will approve.

expect($scope.model.instanceName).toEqual('something');
expect($scope.form.instanceName.$valid).toBe(true);

      

-1


source







All Articles