Get compiled template for controller in unit test

2020-06-22 11:54发布

问题:

I have the following controller:

angular.module('app').controller('userList'
 , ['$scope','appRules',function ($scope,appRules) {
  $scope.isUserInRole=function(user,role){
    console.log("exucuting userIsInRole:",user,role);//never happens
    return  appRules.userIsInRole(user,role);
  }
  window.sscope=$scope;
  console.log($scope.menu);
}]); 

This is used for the following route:

angular.module('app', [,'ui.bootstrap','ngRoute'])
.config(['$routeProvider','$locationProvider'
,function($routeProvider,$locationProvider){
  $locationProvider.html5Mode(true);
  $locationProvider.hashPrefix('!');
  $routeProvider
          .when('/admin/users',{
            templateUrl:'app/js/partials/admin/users/list.html',
            controller:'userList'
          });
}]);

The template would have something that will check if user can see this page or not (even if someone hacks javascript it doesn't matter because when data is fetched or submitted the server checks as well).

<div data-ng-if="!isUserInRole(login.user,'Administrator')" class="red">
  Please log in or log in as another user.
</div>
<div data-ng-if="isUserInRole(login.user,'Administrator')" class="green">
  Page to edit users.
</div>

Now I would like to create a controller in my test and see when it renders if it renders correctly:

  var controller,scope,element;

  beforeEach(inject(function($controller
   ,$rootScope,$compile,appRules) {
    scope=$rootScope;
    scope.login={};
    scope.isUserInRole=appRules.userIsInRole;
    controller = $controller('userList',{
      $scope:scope
    });
    //here I have the controller but how to get the html it generates
    //  when rendering the template?
    element = $compile("<div ng-controller='userList'>"
      +"<div data-ng-view=''></div></div>")(scope);
    //here element isn't rendered at all, works on directives but how
    //  to do this with controllers?
    console.log("element is:",element);
  }));

I would like to write a test like

  it('Check page won\'t show if user is not set or is not Administrator.'
  , function() {
    expect($(element).text().trim()
      .indexOf("Please log in or log in as another user."))
      .toBe(0,'Show login message when login.user is not set.');
  });

Not sure how to get element though.

[update]

I'm actually trying to test a template, because this is used by a controller I was thinking testing it with a controller but as Jon suggested it can just be loaded as template.

How to make it compile and manipulate scope and compile again I don't know. The following:

var el = angular.element(
  $templateCache
  .get('app/js/partials/admin/users/list.html'));
$rootScope = _$rootScope_;
$rootScope.menu={login:{}};
el.scope($rootScope);
$rootScope.$apply();
console.log(el.text());

This gives me the output of both Please log in or log in as another user. and Page to edit users. Looks like element is just the raw element not compiled one.

Trying something like this doesn't do much either:

  beforeEach(inject(function(_$compile_
   , _$rootScope_,appSettings,$templateCache){
    console.log("before each");
    var el = angular.element($templateCache
      .get('app/js/partials/admin/users/list.html'));
    var compile=_$compile_;
    var $rootScope=_$rootScope_;
    $rootScope.login:{};
    $rootScope.isUserInRole=appSettings.userIsInRole;
    //I would think that compile would invoke the ng-if and appSettings.userIsInRole
    //  but no console log in that function is shown.
    var element = compile($templateCache
      .get('app/js/partials/admin/users/list.html'))($rootScope);
    console.log("element is:",element.html());//=undefined    
  }));

回答1:

Add your templates to the cache using ng-templates or ng-html2js so they are available when Karma runs. Then do this:

var scope = null;

beforeEach(inject(function($templateCache,_$compile_,_$rootScope_, _$controller_) {

    //get the template from the cache
    template = $templateCache.get('src/main/app/components/someController/widget-search.tpl.html');

    $compile = _$compile_;
    $rootScope = _$rootScope_;
    $controller = _$controller_;

    scope = $rootScope.new();
    //compile it
    element = $compile(template)(scope);
}));

 //then shove your compiled element into your controller instance



it( 'should be instantiated since it does not do much yet', function(){

            ctrl = $controller('SomeController',
                {
                    $scope: scope,
                    $element: element
                });

             scope.$digest();

            // my controller should set a variable called hasInstance to true when it initializes. Here i'm testing that it does.
            expect( ctrl.hasInstance).toBeTruthy();

        });

For more info, see http://angular-tips.com/blog/2014/06/introduction-to-unit-test-directives/



回答2:

If you are using Karma you could use the ng-html2js karma pre processor which basically runs through your templates and turns them into module which you can include in your test which populate the $templateCache so your template is available.