Adelin
Adelin

Reputation: 19011

How to make $watch function executed in Test?

I am implementing a simple directive that represents a form field with all its extras like label, error field, regex all in a single line.

The directive is as follow:

 <div ng-controller="parentController">

        {{username}}
<!-- the directive -- > 
        <form-field label="Username:" regex="someRegex" constrainsViolationMessage="someValidationMessage" model="username" place-holder="some input value">
        </form-field>
    </div>

Now, I want to test the data binding between the directive scope and the parent scope.

The test is:

it("should bind input field to the scope variable provided by parent scope ! ", function () {
        var formInput = ele.find('.form-input');
        formInput.val("some input");
        expect(ele.find('p').text()).toEqual('some input');
    });

This problem is that I don't know why test don't pass, even the directive works correctly. Here is a fiddle of the directive.

And here is the whole test and test set up.

var formsModule = angular.module('forms', []);

formsModule.controller('parentController', function ($scope) {
});


formsModule.directive('formField', function () {

    var label;
    var constrainsViolationMessage;
    var placeHolder;
    var model;


    return {
        restrict:'E',
        transclude:true,
        replace:false,
        scope:{
            model:'='
        },
        link:function (scope, element, attr) {

            console.log("link function is executed .... ");

            scope.$watch('formInput', function (newValue, oldValue) {
                console.log("watch function is executed .... !")
                scope.model = newValue;
            });
            scope.label = attr.label;
        },
        template:'<div class="control-group ">' +

            '<div class="form-label control-label">{{label}}</div> ' +

            '<div class="controls controls-row"> ' +

            '<input type="text" size="15" class="form-input input-medium" ng-model="formInput"  placeholder="{{placeHolder}}">' +

            '<label class="error" ng-show={{hasViolationConstrain}}>{{constrainsViolationMessage}}</label>' +

            '</div>'
    }
});


beforeEach(module('forms'));

var ele;

var linkingFunction;

var elementBody;


var scope;
var text = "";
var placeHolder = "filed place holder";
var label = "someLabel";
var regex = "^[a-z]{5}$";


beforeEach(inject(function ($compile, $rootScope) {

        scope = $rootScope;


        elementBody = angular.element('<div ng-controller="parentController">' +
            '<p>{{username}}</p>' +
            '<form-field label="Username:" regex="someRegex" constrainsViolationMessage="someValidationMessage" model="username" place-holder="some input value"> </form-field>');

        ele = $compile(elementBody)(scope);
        scope.$digest();
    }
));


afterEach(function () {
    scope.$destroy();
});


iit("should bind input field to the scope variable provided by parent scope ! ", function () {
    var formInput = ele.find('.form-input');
    formInput.val("some input");
    expect(ele.find('p').text()).toEqual('some input');
});

As you can see, I want to assert that form input is reflected in the scope variable set in the 'model' attribute provided by the parent scope.

Am I missing something here ? Thanks for helping me ... !

Upvotes: 12

Views: 7614

Answers (2)

TechieBrij
TechieBrij

Reputation: 99

Use $scope.$digest() after adding condition to execute watch. It will fire watch.

Upvotes: 7

jonc
jonc

Reputation: 795

You're missing the scope.$apply() call after you set the input value, so the change is never getting digested. In the regular application lifecycle this would happen automatically, but you have to manually trigger this in your tests

Take a look at https://github.com/angular/angular.js/blob/master/test/ng/directive/formSpec.js for a ton of examples.

Upvotes: 28

Related Questions