测试具有隔离范围的指令时出现问题

时间:2013-11-23 19:32:38

标签: angularjs angularjs-directive angularjs-scope

在使用隔离范围的指令上运行测试时,似乎无法获得对适当范围的引用。为了清楚起见,对于继承范围的测试,我没有这个问题。

问题表明,对于$httpBackend.expectPOST()调用,我指定了预期的数据。

$httpBackend.expectPOST('/notes/'+scope.note.id+'/replies', {
  content: 'This is the reply'        /*** TEST PASSES IF EXPECTED POST DATA IS NOT SET ***/
}).respond(200, {
  id: 11,
  noteId: 199,
  content: 'This is the reply'
});

当我调用elem.find('[ng-click="saveReply()"]').click()时,范围saveReply()调用,但是没有设置的数据被提交,所以if if我指定它失败的预期数据,但是如果我没有指定它传递的预期数据。显然,要进行更完整的测试,我想执行前者。

如上所述,在调用click()方法之后调用saveReply()方法。这会导致人们认为适当的范围绑定已经到位,但是如果我直接设置范围变量,它仍然没有在范围方法中看到。也许这就是问题/解决方案的所在......

我认为可以通过elem.scope()获得所需的范围引用,但是当我这样做时,elem.scope()与$ rootScope相同。

// FAILS
expect(elem.scope().$id).not.toEqual($rootScope.$id);

有什么想法吗?

'use strict';

describe('Note directive', function () {

  var scope, elem;

  var compile = function($compile, $rootScope, PhotoService) {
    spyOn(PhotoService, 'profilePhotoUrl').andReturn('mypic.jpg');
    scope = $rootScope;
    scope.note = {
      id: 199,
      content: 'This is the note',
      author: {
        firstName: 'Jim',
        lastName: 'Smith',
        name: 'Jim Smith',
        profilePhotoName: 'mypic.jpg'
      },
      replies: [
        {id: 99, content: 'you should not see this on initial load', author: {
          firstName: 'Jim', lastName: 'Smith', name: 'Jim Smith', profilePhotoName: 'mypic.jpg'}
        },
        {id: 99, content: 'but you should see this one', author: {
          firstName: 'Jim', lastName: 'Smith', name: 'Jim Smith', profilePhotoName: 'mypic.jpg'}
        }
      ]
    };
    elem = $compile('<note data="note"></note>')(scope);
    scope.$digest();
  };

  var stubCurrentUser = function ($httpBackend) {
    $httpBackend.expectGET('/users/me').respond({id: 99, name: 'Jim Smith', role: 'owner'});
  };

  beforeEach(module('woddy'));
  beforeEach(module('directive_templates'));
  beforeEach(inject(stubCurrentUser, compile));

  iit('allows the user to reply to a note', inject(function ($httpBackend) {
    $httpBackend.expectPOST('/notes/'+scope.note.id+'/replies', {
      content: 'This is the reply'        /*** TEST PASSES IF EXPECTED POST DATA IS NOT SET ***/
    }).respond(200, {
      id: 11,
      noteId: 199,
      content: 'This is the reply'
    });

    expect(elem.find('[ng-repeat="reply in note.replies"]').length).toBe(2);
    elem.find('[ng-click="reply()"]').click();
    scope.$digest();
    elem.find('[ng-model="content"]').text("This is the reply");
    scope.$digest();
    elem.find('[ng-click="saveReply()"]').click();

    $httpBackend.flush();
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();

    scope.$digest();
    expect(elem.find('[ng-repeat="reply in note.replies"]').length).toBe(3);
  }));

});

指令

'use strict';

angular.module('woddy').directive('note', function() {

  return {
    restrict: 'E',
    scope: {
      note: '=data'
    },
    templateUrl: '/dashboard/note.html',

    controller: function($scope, PhotoService, Reply, CurrentUser) {

      $scope.state = 'show';
      $scope.showAllReplies = false;

      CurrentUser.get().then(function(user) {
        $scope.currentUser = user;
      });

      /**
       * Reply on the note
       */
      $scope.reply = function() {
        $scope.content = '';
        $scope.state = 'add';
      };

      /**
       * Revert back to the default view
       */
      $scope.reset = function() {
        $scope.content = 'Add a comment...';
        $scope.state = 'show';
      };

      $scope.saveReply = function() {
        console.log("It is calling the method")
        var reply = new Reply({
          noteId: $scope.note.id,
          content: $scope.content
        });

        reply.$save(function() {
          var author = $scope.currentUser;
          $scope.note.replies.push({
            content: $scope.content,
            author: {
              id: author.id,
              name: author.name,
              photoName: author.photoName,
              gender: author.gender
            }
          });

          $scope.reset();
        });
      };

    }
  };
});

查看

<div class="note box">
  <img class="author" ng-src="{{authorPhotoUrl()}}" alt="{{note.author.name}}"/>
  <div class="details">
    <div class="name" ng-bind="note.author.name"></div>
    <div class="timestamp" timestamp="{{note.timestamp}}"></div>
  </div>

  <div class="content" ng-bind-html="note.content"></div>

  <div class="note-replies" ng-if="note.replies.length > 0">
    <div class="links">
      <span>{{note.replies.length}} {{note.replies.length == 1 ? 'Comment' : 'Comments'}}</span>
      <a href="#" ng-show="!showAllReplies && note.replies.length > 1" ng-click="showAllReplies = true">Show Comments</a>
      <a href="#" ng-show="showAllReplies && note.replies.length > 1" ng-click="showAllReplies = false">Hide Comments</a>
    </div>

    <div class="replies">
      <div class="reply" ng-repeat="reply in note.replies" ng-show="showAllReplies || $last">
        <img ng-src="{{replyAuthorPhotoUrl(reply.author)}}" alt="{{reply.author.name}}">
        <div class="reply-details">
          <span class="name" ng-bind="reply.author.name"></span>
          <span class="timestamp" timestamp="{{reply.timestamp}}"></span>
          <div class="content" ng-bind-html="reply.content"></div>
        </div>
      </div>
    </div>
  </div>

  <div class="reply-form">
    <div class="reply-content" ng-click="reply()" contenteditable="true" ng-model="content">Add a comment...</div>
    <form ng-show="state == 'add'">
      <div class="text-right">
        <button class="secondary" ng-click="reset()">Cancel</button>
        <button ng-click="saveReply()">Comment</button>
      </div>
    </form>
  </div>
</div>

1 个答案:

答案 0 :(得分:0)

在AngularJs 1.2中,您可以使用elem.isolateScope()来访问指令隔离范围。

这是一个有关的例子: http://plnkr.co/edit/x0c0HYfyVMfsV8LX2bZX?p=preview