Flushing required controllers in control tests

It’s hard for me to try to figure out how I make fun of the required controller for the directive that I wrote that the child is different.

First let me share the directives that I have:

PARENTS

angular
    .module('app.components')
    .directive('myTable', myTable);

function myTable() {
    var myTable = {
        restrict: 'E',
        transclude: {
            actions: 'actionsContainer',
            table: 'tableContainer'
        },
        scope: {
            selected: '='
        },
        templateUrl: 'app/components/table/myTable.html',
        controller: controller,
        controllerAs: 'vm',
        bindToController: true
    };

    return myTable;

    function controller($attrs, $scope, $element) {
        var vm = this;
        vm.enableMultiSelect = $attrs.multiple === '';
    }
}

CHILD

angular
    .module('app.components')
    .directive('myTableRow', myTableRow);

myTableRow.$inject = ['$compile'];

function myTableRow($compile) {
    var myTableRow = {
        restrict: 'A',
        require: ['myTableRow', '^^myTable'],
        scope: {
            model: '=myTableRow'
        },
        controller: controller,
        controllerAs: 'vm',
        bindToController: true,
        link: link
    };

    return myTableRow;

    function link(scope, element, attrs, ctrls) {

        var self = ctrls.shift(),
            tableCtrl = ctrls.shift();

        if(tableCtrl.enableMultiSelect){
            element.prepend(createCheckbox());
        }

        self.isSelected = function () {
            if(!tableCtrl.enableMultiSelect) {
                return false;
            }
            return tableCtrl.selected.indexOf(self.model) !== -1;
        };

        self.select = function () {
            tableCtrl.selected.push(self.model);
        };

        self.deselect = function () {
            tableCtrl.selected.splice(tableCtrl.selected.indexOf(self.model), 1);
        };

        self.toggle = function (event) {
            if(event && event.stopPropagation) {
                event.stopPropagation();
            }

            return self.isSelected() ? self.deselect() : self.select();
        };

        function createCheckbox() {
            var checkbox = angular.element('<md-checkbox>').attr({
                'aria-label': 'Select Row',
                'ng-click': 'vm.toggle($event)',
                'ng-checked': 'vm.isSelected()'
            });

            return angular.element('<td class="md-cell md-checkbox-cell">').append($compile(checkbox)(scope));
        }
    }

    function controller() {

    }
}

So, as you can see, its table row directive, which adds checkbox cells and when switching, is used to populate an array of selected elements bound to the parent table directive area.

When it comes to unit testing the table row directive, I come across solutions where you can mock the required controllers using the data property in the element.

, , , selected :

describe('myTableRow Directive', function() {
  var $compile,
    scope,
    compiledElement,
    tableCtrl = {
      enableMultiSelect: true,
      selected: []
    },
    controller;

  beforeEach(function() {
    module('app.components');
    inject(function(_$rootScope_, _$compile_) {
      scope = _$rootScope_.$new();
      $compile = _$compile_;
    });

    var element = angular.element('<table><tbody><tr my-table-row="data"><td></td></tr></tbody></table>');

    element.data('$myTableController', tableCtrl);
    scope.data = {foo: 'bar'};
    compiledElement = $compile(element)(scope);
        scope.$digest();
    controller = compiledElement.controller('myTableRow');

  });

  describe('select', function(){
    it('should work', function(){
      controller.toggle();
      expect(tableCtrl.selected.length).toEqual(1);
    });
  });
});

:

undefined ( "controller.toggle" )

controller , undefined.

, , - , - ?

UPDATE

:

, AngularJS

controllerAs unit test ?

, , controllerAs:

var element = angular.element('<table><tr act-table-row="data"><td></td></tr></table>');
  element.data('$actTableController', tableCtrl);
  $scope.data = {foo: 'bar'};
  $compile(element)($scope);
  $scope.$digest();
  console.log(element.controller('vm'));

undefined .

2

isolateScope(), undefined angular

, , :

console.log(compiledElement.children().scope().vm);

undefined. compiledElement.children().scope() angular $$ , , , vm, , , ,

3

, , .

, , :

beforeEach(function(){
    var element = angular.element('<table><tr act-table-row="data"><td></td></tr></table>');
    element.data('$actTableController', tableCtrl);
    $scope.data = {foo: 'bar'};
    compiledElement = $compile(element)($scope);
    $scope.$digest();
    element = element.find('act-table-row');
    console.log(element);
    console.log(element.scope()); //returns undefined
});

, , , controllerAs?

+4
2

, . , .controller('myTableRow') , compiledElement . tr child, myTableRow.

. , :

controller = compiledElement.find('tr').controller('myTableRow');

/* Angular App */
(function() {
  "use strict";

  angular
    .module('app.components', [])
    .directive('myTableRow', myTableRow);

  function myTableRow() {
    return {
      restrict: 'A',
      require: ['myTableRow', '^^myTable'],
      scope: {
        model: '=myTableRow'
      },
      controller: controller,
      controllerAs: 'vm',
      bindToController: true,
      link: link
    };

    function link($scope, $element, $attrs, $ctrls) {
      var self = $ctrls.shift(),
        tableCtrl = $ctrls.shift();

      self.toggle = function() {
        // keeping it simple for the unit test...
        tableCtrl.selected[0] = self.model;
      };
    }

    function controller() {}
  }

})();

/* Unit Test */
(function() {
  "use strict";

  describe('myTableRow Directive', function() {
    var $compile,
      $scope,
      compiledElement,
      tableCtrl = {},
      controller;

    beforeEach(function() {
      module('app.components');
      inject(function(_$rootScope_, _$compile_) {
        $scope = _$rootScope_.$new();
        $compile = _$compile_;
      });

      tableCtrl.enableMultiSelect = true;
      tableCtrl.selected = [];

      var element = angular.element('<table><tbody><tr my-table-row="data"><td></td></tr></tbody></table>');

      element.data('$myTableController', tableCtrl);
      $scope.data = {
        foo: 'bar'
      };
      compiledElement = $compile(element)($scope);
      $scope.$digest();
      controller = compiledElement.find('tr').controller('myTableRow');
      //console.log(controller); // without the above .find('tr'), this is undefined
    });

    describe('select', function() {
      it('should work', function() {
        controller.toggle();
        expect(tableCtrl.selected.length).toEqual(1);
      });
    });

  });

})();
<link rel="stylesheet" href="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine.css" />
<script src="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine.js"></script>
<script src="//cdn.jsdelivr.net/jasmine/2.0.0/jasmine-html.js"></script>
<script src="//cdn.jsdelivr.net/jasmine/2.0.0/boot.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-mocks.js"></script>
Hide result
+3

, angular, .

: ( )

angular.module('annotatedimage').directive('annotatedImage', function() {
  function AnnotatedImageController(scope) {}

  return {
    {
      restrict: 'E',
      template: [
        '<annotated-image-controls annotations="configuration.annotations"></annotated-image-controls>',
        '<annotated-image-viewer src="configuration.image" annotations="configuration.annotations"></annotated-image-viewer>',
        '<annotated-image-current></annotated-image-current>'
      ].join('\n'),
      controller: ['$scope', AnnotatedImageController],
      scope: {
        configuration: '='
      }
    }
  };
});
Hide result

annotatedImageController, annotatedImageViewer annotatedImageCurrent, .

 angular.module('annotated-image').directive('annotatedImageControls', function() {
   function link(scope, el, attrs, controller) {
     scope.showAnnotations = function() {
       controller.showAnnotations();
     };

     controller.onShowAnnotations(function() {
       scope.viewing = true;
     });
   }

   return {
     restrict: 'E',
     require: '^annotatedImage',
     template: [
       '<div>',
       '<span span[data-role="show annotations"]     ng-click="showAnnotations()" ng-hide="viewing">Show</span>',
       '<span span[data-role="hide annotations"] ng-click="hideAnnotations()" ng-show="viewing">Hide</span>',
       '<span ng-click="showAnnotations()">{{ annotations.length }} Annotations</span>',
       '</div>'
     ].join('\n'),
     link: link,
     scope: {
       annotations: '='
     }
   };
 });
 angular.module('annotated-image').directive('annotatedImageViewer', function() {
   function link(scope, el, attrs, controller) {
     var canvas = el.find('canvas');
     var viewManager = new AnnotatedImage.ViewManager(canvas[0], scope.src);

     controller.onShowAnnotations(function() {
       viewManager.showAnnotations(scope.annotations);
     });
   }

   return {
     restrict: 'E',
     require: '^annotatedImage',
     template: '<canvas></canvas>',
     link: link,
     scope: {
       src: '=',
       annotations: '='
     }
   };
 });
Hide result

annotatedImageCurrent

<parent-component>
  <child-component></child-component>
  <another-child-component></another-child-component>
</parent-component>
Hide result

 module.directive('parentComponent', function() {
   function ParentComponentController(scope) {
     // initialize scope
   }

   ParentComponentController.prototype.doSomething = function() {
     // does nothing here
   }

   return {
     restrict: 'E',
     controller: ['$scope', ParentComponentController],
     scope: {}
   };
 });
Hide result

module.directive('childComponent', function() {
  function link(scope, element, attrs, controller) {
    controller.doSomething();
  }

  return {
    restrict: 'E',
    require: '^parentComponent',
    link: link,
    scope: {}
  }
});
Hide result
0

Source: https://habr.com/ru/post/1655179/


All Articles