34

I have a directive that builds a form:

app.directive('config', function() {
  return {
    restrict: 'E',
    scope: {
      data: '='
    },
    template: '<form name="configForm">' +
      '<input type="number" max="10" ng-model="config.item" name="configItem"/>' +
      '<div class="form-error" ng-show="configForm.$error.max">Error</div>' + 
      '</form>',
    controller: 'ConfigDirectiveController',
  };
});

What I want to do is validate via a unit test that the error message will show up given some input. With angular 1.2 I could modify $scope.config.item and it would update the view value and show the error.

As near as I can tell, with angular 1.3, if the model does not pass validation the view value does not get updated...so I need to modify the view value to make sure the error message shows up.

How can I get access to the "configItem" input so that I can set the view value to ensure that the error message will show up?

Edited to show unit test

I see that the value is set properly, but the error still has an ng-hide applied to the tag. When I am viewing the page and manually changing the input value, the ng-hide will be removed and the error will display if I enter in something greater than 10.

  beforeEach(inject(function($compile, $rootScope) {
      element = angular.element('<config data="myData"></config>');
      $scope = $rootScope.$new();
      $scope.myData = {};
      element = $compile(element)($scope);
    }));

    it('should warn that we have a large number', function() {
      var input = element.find('[name="configItem"]')[0];
      $scope.$apply(function() {
        angular.element(input).val('9000000001');
      });
      errors = element.find('[class="form-error ng-binding"]');
      expect(errors.length).toBe(1);
    })

2 Answers 2

63

Here's how I've been unit testing my input-based directives (Lots of code omitted for clarity!) The important line you are after is:

angular.element(dirElementInput).val('Some text').trigger('input');

Here's the full unit test:

  it('Should show a red cross when invalid', function () {

    dirElement = angular.element('<ng-form name="dummyForm"><my-text-entry ng-model="name"></my-text-entry></ng-form>');

    compile(dirElement)(scope);
    scope.$digest();

    // Find the input control: 
    var dirElementInput = dirElement.find('input');

    // Set some text!
    angular.element(dirElementInput).val('Some text').trigger('input');
    scope.$apply();

    // Check the outcome is what you expect! (in my case, that a specific class has been applied)
    expect(dirElementInput.hasClass('ng-valid')).toEqual(true);
  });
Sign up to request clarification or add additional context in comments.

5 Comments

I'll mark it as answered as it does get me part-way there. It does modify the input element however it is not triggering validation. Rather it is not causing configForm.$error.max to be marked as true...so my error is still hidden.
It may be because you're not using the directive's compiled output. In your examples you are overwriting the directives input with the compiled output (var element), sometimes not (dirElement). If you use the compiled result should get you closer to the angular actual form behaviour.
n.b that this example is leveraging .trigger() which assumes you are using Angular with jQuery. If you are using the jqlite implementation you will need to manually dispatch the error with something like angular.element(dirEelementInput)[0].dispatchEvent(new Event('input')); instead.
could be also ` scope.configForm.configItem.$setViewValue("some value", true); scope.$apply(); ` second parameter trigger handlers
also important to do scope.$apply()!
31

The previous answer is correct if you are using Angular with jQuery but for Angular without jQuery (using jqlite) you can use triggerHandler instead (see here for full API)

it('foos to the bar', function() {
  el.val('Foo').triggerHandler('input');

  // Assuming el is bound to scope.something using ng-model ...
  expect(scope.something).toBe('Foo');
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.