Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing Unit test for Password Matching directive

I have a directive here I'm trying to write a unit test for - first time doing this type of thing. I'm not sure how to go about it. Here's the directive code and HTML :

app.directive('passwordMatch', [function () {
    return {
        restrict: 'A',
        scope:true,
        require: 'ngModel',
        link: function (scope, elem, attrs, control) {
            var checker = function () {
                var e1 = scope.$eval(attrs.ngModel);
                var e2 = scope.$eval(attrs.passwordMatch);
                if(e2!=null)
                return e1 == e2;
            };
            scope.$watch(checker, function (n) {
                control.$setValidity("passwordNoMatch", n);
            });
        }
    };
}]);
<form name="signupForm">
<div class="form-group">
		
	    <div class="col-sm-7">
	        <span class="block input-icon input-icon-right">
	    <input type="password" class="register" name="password" placeholder="Password" ng-model="signup.password" required/>
			</span>
	    </div>
	</div>
	<div class="form-group">	
	    <div class="col-sm-7">
	        <span class="block input-icon input-icon-right">
			   <input type="password" class="register" name="password2" placeholder="Confirm Password" ng-model="signup.password2" password-match="signup.password" required/>                                
			   <small class="errorMessage" data-ng-show="signupForm.password2.$dirty && signupForm.password2.$error.passwordNoMatch && !signupForm.password2.$error.required"> Password do not match.</small>
			</span>
	    </div>
	</div>
</form>

And here's what I'm trying for a test. So for, it is giving me an error reading TypeError: 'undefined' is not an object (evaluating 'scope.signup.password = '123'')

describe('passwordMatch Directive - ', function() {
    
    var scope, $compile, $window, element;
       
    beforeEach(function() {
      module('myApp');
      inject(function(_$compile_, _$rootScope_, _$window_) {
        $compile = _$compile_;
        scope = _$rootScope_.$new();
        $window = _$window_;
      })
     
    })

    it('should indicate invalid when the passwords do not match.', function() {
        scope.signup.password = '123';
        scope.signup.password2 = '1234';
        element = $compile(angular.element('<input type="password" class="register" name="password" placeholder="Password" ng-model="signup.password" required/> <input type="password" class="register" name="password2" placeholder="Confirm Password" ng-model="signup.password2" password-match="signup.password" required/>'))(scope);
        scope.$apply();
        console.debug('element html - ' + element.html());
        expect(element.html().indexOf('ng-invalid')).toBeGreaterThan(0);
    });

    it('should indicate valid when the passwords do not match.', function() {
        scope.signup.password = '123';
        scope.signup.password2 = '123';
        element = $compile(angular.element('<input type="password" class="register" name="password" placeholder="Password" ng-model="signup.password" required/> <input type="password" class="register" name="password2" placeholder="Confirm Password" ng-model="signup.password2" password-match="signup.password" required/>'))(scope);
        scope.$apply();
        console.debug('element html - ' + element.html());
        expect(element.html().indexOf('ng-valid')).toBeGreaterThan(0);
    });
});

Some help would be greatly appreciated

EDIT: I just noticed when commenting out scope.signup.password = '123' and such, the debug statement does not return anything - just DEBUG: 'element html - ', so element.html() is not doing anything?

like image 943
realization Avatar asked Jul 12 '15 21:07

realization


1 Answers

I've not done testing for a while but have you tried defining the scope.signup object first, perhaps in your beforeEach block. So, something like,

describe('passwordMatch Directive - ', function() {

    var scope, $compile, $window, element, elm;

    beforeEach(function() {
      module('myApp');
      inject(function(_$compile_, _$rootScope_, _$window_) {
        $compile = _$compile_;
        scope = _$rootScope_.$new();
        $window = _$window_;
      });


      // define scope.signup as an empty object literal to which
      // you can add properties later 
      scope.signup = {};

      // don't think you meant to have this so commenting it out
      //elm 
    })

    it('should indicate invalid when the passwords do not match.', function() {
        scope.signup.password = '123';
        scope.signup.password2 = '1234';
        element = $compile(angular.element('<input type="password" class="register" name="password" placeholder="Password" ng-model="signup.password" required/> <input type="password" class="register" name="password2" placeholder="Confirm Password" ng-model="signup.password2" password-match="signup.password" required/>'))(scope);
        scope.$apply();
        console.debug('element html - ' + element.html());
        expect(element.html().indexOf('ng-invalid')).toBeGreaterThan(0);
    });

    // etc...
});

EDIT

I think you need to have your inputs inside a form. Try this...

describe('passwordMatch Directive - ', function() {

    var scope, $compile, $window, element, html;

    beforeEach(function() {

      module('myApp');

      inject(function(_$compile_, _$rootScope_, _$window_) {
        $compile = _$compile_;
        scope = _$rootScope_.$new();
        $window = _$window_;
      });

      scope.signup = {};

      // inputs need to be within a form for you directive to work
      html =  '<form name="signupForm">' +
               '<input type="password" class="register" name="password" placeholder="Password" ng-model="signup.password" required/>' +
               '<input type="password" class="register" name="password2" placeholder="Confirm Password" ng-model="signup.password2" password-match="signup.password" required/>' +
              '</form>';  

    });

    it('should indicate invalid when the passwords do not match.', function() {
        scope.signup.password = '123';
        scope.signup.password2 = '1234';
        element = $compile(angular.element(html))(scope);
        scope.$apply();
        console.debug('element html - ' + element.html());
        expect(element.html().indexOf('ng-invalid')).toBeGreaterThan(0);
    });

    it('should indicate valid when the passwords do not match.', function() {

        scope.signup.password = '123';
        scope.signup.password2 = '123';

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

        scope.$apply();
        console.debug('element html - ' + element.html());
        expect(element.html().indexOf('ng-valid')).toBeGreaterThan(0);
    });
});

EDIT 2

You can access the FormController properties directly from the scope too and make assertions on these properties rather than using element.html(). Makes it a bit more readable.

it('should indicate invalid when the passwords do not match.', function() {


        scope.signup.password = '123';
        scope.signup.password2 = '1234';

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

        // expect(element.html().indexOf('ng-invalid')).toBeGreaterThan(0);

        expect(scope.signupForm.$valid).toBe(false);
        expect(scope.signupForm.$invalid).toBe(true);

    });

    it('should indicate valid when the passwords do not match.', function() {

        scope.signup.password = '123';
        scope.signup.password2 = '123';

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

        scope.$apply();

        //expect(element.html().indexOf('ng-valid')).toBeGreaterThan(0);

        expect(scope.signupForm.$valid).toBe(true);
        expect(scope.signupForm.$invalid).toBe(false);
    });
like image 141
Matt Herbstritt Avatar answered Sep 28 '22 08:09

Matt Herbstritt