Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I simulate blur when testing directives in angularjs?

The problem

I am trying to test some directives (code for both below). One of them is an "email" (called "epost" in the code(norwegian)) directive. The solution to this should work for all of them, so I am keeping it to this one for now.

Technologies: Angularjs, Jasmine, Requirejs, (grunt & karma running in Chrome)

The directive validates email addresses in two ways; on upshift and on blur. I can test the upshift without problems as you can see in the test below, but I can't figure out how to simulate a blur so the bind('blur') in the directive runs.

What I have done

I have tried to catch the compiled element like this:

elem    = angular.element(html);
element = $compile(elem)($scope);

And then in the test i tried several permutations to trigger the blur with a console log just inside the bind function in the directive. None of the below works. It does not trigger.

elem.trigger('blur');           
element.trigger('blur');
elem.triggerHandler('blur');
element.triggerHandler('blur');
element.blur();
elem.blur();

I based the injection and setup on this: To test a custom validation angularjs directive

The email directive in angularjs wrapped in requirejs

define(function() {

var Directive = function() {

return {
  require: 'ngModel',
  link: function(scope, elem, attrs, ctrl) {
    var pattern = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;

    elem.bind('blur', function() {
      scope.$apply(function () {
        if (!elem.val() || pattern.test(elem.val())) {
          ctrl.$setValidity('epost', true);
        } else {
          ctrl.$setValidity('epost', false);
        }
      });
    });

    ctrl.$parsers.unshift(function(viewValue) {
      if (pattern.test(viewValue)) {
        ctrl.$setValidity('epost', true);
        return viewValue;
      } else {
        return undefined;
      }
      });
    }
  };
};

return Directive;

});

The test (using jasmine and requirejs)

define([
'Angular', 
'AngularMocks',
], function () {

describe('Directives', function () {

    var $scope;
    var form;

    beforeEach(module('common'));
    beforeEach(function () {

        var html = '<form name="form">';
        html += '<input type="text" id="epost" name="epost" epost="" ng-model="model.epost"/>';
        html += '</form>';

        inject(function ($compile, $rootScope) {
            $scope = $rootScope.$new();

            $scope.model = { 
                epost: null
            };

            // Compile the element, run digest cycle
            var elem = angular.element(html);
            $compile(elem)($scope);
            $scope.$digest();

            form = $scope.form;

        });
    }); 

    describe('(epost) Given an input field hooked up with the email directive', function () {

        var validEmail = '[email protected]';
        var invalidEmail = 'asdf@asdf';

        it('should bind data to model and be valid when email is valid on upshift', function () {
            form.epost.$setViewValue(validEmail);
            expect($scope.model.epost).toBe(validEmail);
            expect(form.epost.$valid).toBe(true);
        });
        });
    });
});
like image 288
lizter Avatar asked Jul 15 '13 13:07

lizter


1 Answers

I have been able to figure out where I went wrong after some breakpoint debugging.

The "element" item I get out using the approach described in the top of the question is not actually the directive it self. It's an object which wraps the form and the directive.

Like this

{ 0:   // The form
  { 0: // The directive (input element)
    {  
    }
  }
}

To actually simulate a blur on the directive it self, I did something like this

var directiveElement = $(element[0][0]);
directiveElement.blur();

After getting the element I wanted, and wrapping it in a jQuery object (may be optional), it worked like a charm. I then used the approach like in the test in the question with $setViewValue and checked the model value like this.

form.epost.$setViewValue('[email protected]');
directiveElement.blur();
expect($scope.model.epost).toBe('[email protected]');
expect($scope.form.epost.$valid).toBeTruthy();

Hope this could be of help to others trying to figure the directive testing out.

like image 190
lizter Avatar answered Oct 13 '22 01:10

lizter