单元测试AngularJS指令,模板包含ng-if

时间:2016-01-18 05:54:48

标签: angularjs unit-testing jasmine

我正在尝试测试下一个角度指令。它会收听 ui-route $ stateChange 事件以显示进度指示器。

angular.module('sm.components.progress', ['ui.router'])

.directive('smProgress', function($rootScope) {
  return {
    restrict: 'E',
    replace: true,
    template: '<div class="in-progress" ng-if="isRouteLoading">' +
                '<span>.</span><span>.</span><span>.</span>' +
              '<div/>',

    controller: function($scope) {
      $scope.isRouteLoading = false;

      $rootScope.$on('$stateChangeStart',
        function() {
          $scope.isRouteLoading = true;
        });

      $rootScope.$on('$stateChangeSuccess',
        function() {
          $scope.isRouteLoading = false;
        });
    }
  };
});

这是我的测试代码:

describe('smProgress', function() {
  var $compile, $rootScope, element, scope;

  beforeEach(module('sm.components.progress'));

  beforeEach(inject(function(_$compile_, _$rootScope_) {
    $compile = _$compile_;
    $rootScope = _$rootScope_;
    scope = $rootScope.$new();
  }));

  afterEach(function() {
    element.remove();
  });


  it('$rootScope.isRouteLoading should be false on start',
     function() {

    element = $compile('<sm-progress></sm-progress>')(scope);
    scope.$digest();

    expect(scope.isRouteLoading).toBe(false);

    $rootScope.$emit('$stateChangeStart');

    //The directive listen to the $stateChangeStart and     
    //scope.isRouteLoading become true as expected
    expect(scope.isRouteLoading).toBe(true);
    scope.$digest();

    // checks if the template is rendered when ng-if evaluates true
    // how?

  }));

});

模板中的 ng-if 开始评估false,因此该元素将从DOM中删除。当我手动发出$stateChangeStart事件时,指令监听器工作,但我找不到DOM中的元素。

ng-if 再次评估为true时,如何检查模板是否已添加到DOM?

1 个答案:

答案 0 :(得分:9)

嗯,这是我找到的解决方案。

重要的是要注意两件事:

  1. 指令将替换设置为<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css"> <script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script> <p> See <a href='http://stackoverflow.com/q/34818540/841108'>this SO question</a> </p> <h2> my menu </h2> <p class='explaincl'> First, try clicking the button, and do nothing more: the menu disappears in 10 sec. Then, try again, click the button, move the mouse inside the menu, it is still disappearing but I want it to stay!</p> <button id='mybutton_id'>click me</button> <div id='mymenudiv_id'> Here </div> <p id='message_id'> </p>
  2. 该模板具有 ng-if 指令。
  3. 这就是:

    在测试中,编译指令时:

    true

    element = $compile('<sm-progress></sm-progress>')(scope); 替换为:

    <sm-progress></sm-progress>

    同时,ng-if为false,因此最终呈现的代码只是一个注释。这就是 ng-if 自然地工作的方式。

    <div class="in-progress" ng-if="isRouteLoading">
      <span>.</span><span>.</span><span>.</span>
    <div/>
    

    当触发 $ stateChangeStart 时, scope.isRouteLoading 转到<!-- ngIf: isRouteLoading --> ,这会使 ng-if 重新插入模板DOM。但在这一点上元素等于:

    true

    无法在其中找到指令模板。

    <!-- ngIf: isRouteLoading -->
    

    解决方案是将html文本包装在 div 或任何其他元素中。因此ng-if将在其中运行,我们可以检查当时发生的事情。

    // THE TEST FAILS
    var progressEl = angular.element(element).find('in-progress');
    expect(progressEl.length).toBeGreaterThan(0);
    

    jsfiddle.net,代码工作http://jsfiddle.net/fwxgyjwc/13/

    我希望我已经解释得很好。我的英语不是很好。