angularjs - 使用 $parsers(和 $formatters)对指令进行单元测试

标签 angularjs angularjs-directive jasmine karma-jasmine

我有以下指令:

!(function (window, angular) {
    'use strict';

    /**
     * @ngdoc directive
     * @name app.directive:social
     * @description
     * # social
     */
    angular.module('app')
        .directive('social', function(social_network_conf) {
            return {
                restrict: 'A',
                scope: {
                    social: "@"
                },
                require: 'ngModel',
                controller: function($scope, $element){
                    //for tests only
                    $scope.render = function(){
                        //how to I get the ngModel here
                        ngModel.$render();
                    };

                    $scope.setViewValue = function(val){
                        ngModel.$setViewValue(val);
                    };
                },
                link: function(scope, element, attr, ngModel) {

                    ngModel.$formatters.push(function(value) {// from model to view
                        value = value.trim();
                        if(value){
                            if (value.indexOf(social_network_conf.matcher) === 0){
                                var split_link = value.split(social_network_conf.divider);
                                return split_link[split_link.length-1];
                            }
                            else{
                                return value;
                            }
                        }
                    });

                    ngModel.$parsers.push(function(value) { // from view to model
                        value = value.trim();
                        if(value){
                            if (value.indexOf(social_network_conf.matcher) === 0){
                                return value;
                            }
                            else{
                                return social_network_conf.prefix + scope.social +
                                       social_network_conf.suffix + value;
                            }
                        }
                    });
                }
            };
        });
}(window, window.angular));

测试如下:

'use strict';

describe('Directive: social', function () {

  // load the directive's module
  beforeEach(module('app'));

  var element,
    social_network_conf,
    linker,
    scope,
    $httpBackend;

  beforeEach(inject(function ($rootScope, _$httpBackend_, _social_network_conf_) {
    scope = $rootScope.$new();
      social_network_conf = _social_network_conf_;
      //Must be an object to make use of prototypical inheritence for out-side-of-isolate-scope access
      scope.models = {};

      $httpBackend = _$httpBackend_;
      $httpBackend.whenGET(/views\/social.html/).respond('<div></div>');
      $httpBackend.whenGET(/views\/navigation.html/).respond('<div></div>');

  }));

  it('It should convert ngModel into full HTTP address notation', inject(function ($compile) {
    element = angular.element('<input social="test_network" ng-model="models.test_network"></social>');
    linker = $compile(element);
    element = linker(scope);
    scope.$apply(function(){
        element.val('test');
    });
    scope.$digest();
    expect(scope.models.test_network).toBe(social_network_conf.prefix + 'test' +
          social_network_conf.suffix);
//    expect(element.text()).toBe('this is the social directive');
  }));
});

问题在于这些行:

scope.$apply(function(){
    element.val('test');
});

实际上并不调用我定义的 $parser。

尽管使用API​​为指令创建了一个 Controller 来调用ngModel.$renderngModel.$setViewValue,但我无法在指令 Controller 没有丑陋的黑客攻击。

最佳答案

2种可能的解决方案:

首先是将元素包装在表单中,并为输入字段和表单分配一个 name 属性,然后按如下方式访问输入字段:

scope.form_name.input_name.$setViewValue('value')

工作代码:

'use strict';

describe('Directive: social', function () {

  // load the directive's module
  beforeEach(module('app'));

  var element,
    social_network_conf,
    linker,
    scope,
    $compile,
    $body,
    html,
    $httpBackend;

  beforeEach(inject(function ($rootScope, _$compile_, _$httpBackend_, _social_network_conf_) {
    scope = $rootScope.$new();
      social_network_conf = _social_network_conf_;
      //Must be an object to make use of prototypical inheritence for out-side-of-isolate-scope access
      scope.models = {};
      $compile = _$compile_;
      $httpBackend = _$httpBackend_;
      $body = $('body');
      $httpBackend.whenGET(/views\/social.html/).respond('<div></div>');
      $httpBackend.whenGET(/views\/navigation.html/).respond('<div></div>');
      $body.empty();
      html = '<form name="testForm">' +
                '<input social="test_network" name="test" ng-model="models.test_network">' +
             '</form>';

  }));

  it('It should convert ngModel into full HTTP address notation', function () {

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

    var viewValue = 'test',
        input = element.find('input');

    scope.models.test_network = viewValue;
    scope.$digest();

    scope.testForm.test.$setViewValue(viewValue);

    scope.$digest();
    expect(scope.models.test_network).toBe(social_network_conf.prefix + input.isolateScope().social +
          social_network_conf.suffix + viewValue);

  });

或者,第二个提供了在元素上分派(dispatch)输入事件 - 对我来说 Angular 1.3 不起作用。 This YouTube video 中对此进行了演示。 .

 element.val('test');
 element.trigger('input');

关于angularjs - 使用 $parsers(和 $formatters)对指令进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26659042/

相关文章:

javascript - Angular Material datepicker 过滤器特定日期

jasmine - 未捕获的 ReferenceError : Zone is not defined (Jasmine)

javascript - 使用 Materialize CSS 和 Angular 进行导航栏搜索

javascript - mootools 1.4.2 和 angular 1.3 在 ie8 中一起玩得很好

rest - 单个服务 AngularJS 的多个路由 URL

javascript - 在 click 方法外部触发 e.preventDefault()

javascript - 启动 Angular Controller 的更好方法

javascript - 使用自定义指令使用 ng-repeat 显示 2 个不同的表

javascript - 一个失败的测试会导致其他异步测试失败

jquery - jQuery document.ready 中的 Coffeescript 调用函数