0

How can I test this directive ?

angular.module('uiApp')
.directive('uppercase', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ngModelCtrl) {
            var uppercase = function (value) {
                var uppercase = value.toUpperCase();
                if (uppercase !== value) {
                    ngModelCtrl.$setViewValue(uppercase);
                    ngModelCtrl.$render();
                }
                return uppercase;
            };
            ngModelCtrl.$parsers.push(uppercase);
        }
    };
});

I have tried this approach but it doesn't work:

it('should transform to uppercase', inject(function ($compile) {
    element = angular.element('<input type="text" ng-model="test" ng-init="test=\'test\'" uppercase>');
    element = $compile(element)(scope);
    expect(element.text()).toBe('TEST');
}));
amb
  • 1,599
  • 2
  • 11
  • 18

2 Answers2

3

I have got it:

it('should transform to uppercase', inject(function ($compile) {
    scope.test = '';
    element = angular.element('<input type="text" ng-model="test" uppercase>');
    element = $compile(element)(scope);
    element.val('test').triggerHandler('input');
    expect(element.val()).toBe('TEST');
    expect(scope.test).toBe('TEST');
}));

And it works. Apparently trigger('input') is the key.

Alex Carod
  • 131
  • 1
  • 8
amb
  • 1,599
  • 2
  • 11
  • 18
1

You need to append uppercase to $formatters in the link function, not $parsers:

ngModelCtrl.$formatters.push(uppercase);

Then test it like this:

it('should transform to uppercase', inject(function ($compile, $rootScope, $timeout) {
    $rootScope.test = 'test';
    element = angular.element('<input type="text" ng-model="test" uppercase>');
    element = $compile(element)($rootScope);
    $rootScope.$apply();
    expect(element.val()).toBe('TEST');
}));
Ye Liu
  • 8,946
  • 1
  • 38
  • 34
  • No, I still get the error `Expected '' to be 'TEST'.` – amb May 14 '14 at 14:04
  • The same error. If I add `$rootScope.$digest()` before `$rootScope.$apply(...)` at least I have the error `Expected 'test' to be 'TEST'` – amb May 14 '14 at 14:12
  • @amb I found your implementation is not quite right, I updated my answer to address that. – Ye Liu May 14 '14 at 14:41
  • I think there is a still a deficiency in the implementation. I believe that it should be able to work with ng-init. But if you look at this [plunker](http://plnkr.co/edit/5Ic8yoqi20MR5ej8GRxs?p=preview) you can also see that the view value is not updated to be uppercase when the page first loads – JoseM May 14 '14 at 14:50
  • @JoseM and Ye Liu, yes that is intended. To appply uppercase when the page loads I can call at the end: `uppercase(scope[attrs.ngModel]);` – amb May 14 '14 at 14:53
  • @JoseM There is a small difference between your implement and amb's. If you change ngModelCtrl.$viewValue = uppercase` to `ngModelCtrl.$setViewValue(uppercase)`, you'll see what you're expecting. – Ye Liu May 14 '14 at 14:56
  • @Ye Liu I have tested your code again but it still fails even if I modify the directive to uppercase on page load. Even though I finally figured it out, I thank you. – amb May 14 '14 at 14:59
  • @amb This test in your own answer is suggesting that your directive is transforming the user input, but the test in your original question is transforming the value from ngModel. What is the real purpose of your directive? – Ye Liu May 14 '14 at 15:03
  • @YeLiu The value from ngModel is the user input or the value from the model. Since it's a 2-way data binding it will uppercase the value from user input, or the value from model but not on the first page load (considering the value from model). I don't understand what is not clear. – amb May 14 '14 at 15:08
  • @YeLiu after I looked over at my initial test, I get it why it's confusing. But the idea is that it should work both ways - user input and model but only after the first page load. I had no idea what to write in the test so it was a bad example. – amb May 14 '14 at 15:12
  • @YeLiu I have up-voted your answer since it helped me. – amb May 14 '14 at 15:20
  • 1
    @amb I have created a [plunk](http://plnkr.co/edit/EHkJdxW0Z2mERXg2LfEe) to show you the difference between $parsers and $formatters. $parsers are responsible for getting dom changes to model, $formatters are responsible for formatting model value to dom. If you want to transform text both ways, you need to append uppercase to both $parsers and $formatters. – Ye Liu May 14 '14 at 15:48
  • @YeLiu thanks. Yes I forgot about the $formatters. I was more interested in modifying the user input since data retrieved from the database would already be in uppercase. The directive follows this example: http://stackoverflow.com/questions/15242592/angular-js-how-to-autocapitalize-an-input-field – amb May 14 '14 at 16:02
  • @YeLiu I think I will let it as it is since data from model will always be in uppercase. – amb May 14 '14 at 16:03