How to trigger ng-change in directive test in AngularJS

angularjs, angularjs-directive

Solution

From your explanatory comment:

All I want to do in directive's test is to check that `doIt` is called when user changes the input.

Whether or not the expression indicated by `ng-change` is correctly evaluated or not is really the responsibility of the `ngModel` directive, so I'm not sure I'd test it in this way; instead, I'd trust that the `ngModel` and `ngChange` directives have been correctly implemented and tested to call the function specified, and just test that calling the function itself affects the directive in the correct manner. An end-to-end or integration test could be used to handle the full-use scenario.

That said, you can get hold of the `ngModelController` instance that drives the `ngModel` change callback and set the view value yourself:

it('trigger doIt', function() {
  var ngModelController = el.find('input').controller('ngModel');
  ngModelController.$setViewValue('test');
  expect($scope.youDidIt).toBe(true);
});

As I said, though, I feel like this is reaching too far into `ngModel`'s responsibilities, breaking the black-boxing you get with naturally composable directives.

Example: http://plnkr.co/edit/BaWpxLuMh3HvivPUbrsd?p=preview

[Update]

After looking around at the AngularJS source, I found that the following also works:

it('trigger doIt', function() {
  el.find('input').trigger('input');
  expect($scope.youDidIt).toBe(true);
});

It looks like the event is different in some browsers; `input` seems to work for Chrome.

Example: http://plnkr.co/edit/rbZ5OnBtKMzdpmPkmn2B?p=preview

Here is the relevant AngularJS code, which uses the `$sniffer` service to figure out which event to trigger:

changeInputValueTo = function(value) {
  inputElm.val(value);
  browserTrigger(inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
};

Even having this, I'm not sure I'd test a directive in this way.

Problem

I have the following AngularJS directive that creates an `input` element. Input has `ng-change` attribute that runs `doIt()` function. In my directive's unit test I want to check if `doIt` function is called when users changes the input. But the test does not pass. Though it works in the browser when testing manually. Directive: ``` ... template: "<input ng-model='myModel' ng-change='doIt()' type='text'>" ``` Test: ``` el.find('input').trigger('change') // Dos not trigger ng-change ``` Live demo (ng-change): http://plnkr.co/edit/0yaUP6IQk7EIneRmphbW?p=preview Now, the test passes if I manually bind `change` event instead of using `ng-change` attribute. ``` template: "<input ng-model='myModel' type='text'>", link: function(scope, element, attrs) { element.bind('change', function(event) { scope.doIt(); }); } ``` Live demo (manual binding): http://plnkr.co/edit/dizuRaTFn4Ay1t41jL1K?p=preview Is there a way to use `ng-change` and make it testable? Thank you.

Original source