lundi 21 décembre 2015

Unit test isolate scope directive with passed in function calls

I have a directive that has a directive controller associated with it. It has an isolate scope in which a function gets passed into the form-submit attribute. In my test, I want to test that the right functions are being called, but can't figure it out.

The my-example directive

function myExample(MyService) {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    templateUrl: 'form.html',
    scope: {
      formSubmit: '&'
    },
    require: ['form', 'myExample'],
    controller: function($scope, $element, $attrs) {
      var vm = this;
      vm.data = {};

      vm.directiveCtrlSubmit = function() {
        // i want to test MyService call
        MyService.submit(vm.data)
          .then(function(res) {
            // call parent form submit function
            // i want to test this
            vm.formSubmit();
          }, function(err) {
            // i want to test this gets called as well
            $scope.showError(err);
          });
      };
    },
    link: function(scope, element, attrs, ctrls){
      var formCtrl = ctrls[0];
      var directiveCtrl = ctrls[1];

      // directive link submit function
      scope.directiveLinkSubmit = function() {
        // call directive controller submit
        if (formCtrl.$valid) {
          directiveCtrl.directiveCtrlSubmit();
        }
      };

      // error function
      scope.showError = function(err) {
        // do something
      }
    },
    controllerAs: 'myExampleCtrl',
    bindToController: true
  };
}

angular.module('example')
  .directive('myExample', [
    'MyService',
    myExample
  ]);

The usage of the directive:

<div ng-controller="MyParentCtrl as parentCtrl">
  <my-example form-submit="parentCtrl.parentSubmit()"></my-example>
</div>

Inside the directive template

<form novalidate>
  // example input fields
  <input type="text" ng-model="myExampleCtrl.data.first_name" />
  <input type="text" ng-model="myExampleCtrl.data.last_name" />
  <button type="button" class="my-submit-btn" ng-click="directiveLinkSubmit()">Submit</button>
</form>

Inside the test

How do I test that MyService and the parent function vm.formSubmit (which would be myFunction inside the test) gets called??

describe('Directive: my-example', function() {
  var element;
  var compile;
  var scope;
  var MyService;
  var $q;

  beforeEach(module('example'));

  // Manually compile and link our directive
  function getCompiledElement(attrs, template) {
    var compiledElement
    var validTemplate;

    // template
    validTemplate = '<my-example ' + (attrs || '') + '></my-example>';
    compiledElement = compile(template || validTemplate)(scope);
    scope.$digest();

    return compiledElement;
  }

  // internal helper
  function getFormElements(root, selector) {
    return angular.element(root[0].querySelectorAll(selector));
  }

  beforeEach(function() {
    inject(function($compile, $rootScope, _MyService_, _$q_) {
      compile = $compile;
      scope = $rootScope.$new();
      $q = _$q_;
      MyService = _MyService_;
    });

  });

  describe('submitting the form', function() {
    var deferred;

    beforeEach(function() {
      deferred = $q.defer();

      spyOn(MyService, 'submit').and.returnValue(deferred.promise);
    });

    it('should call the right functions', function() {
      // setup form element and compile
      var form = getCompiledElement('form-submit="myFunction()"');
      var formCtrl = form.controller('form');
      var directiveCtrl = form.controller('myExample');
      var formBtn = getFormElements(form, '.my-submit-btn');

      spyOn(directiveCtrl, 'directiveCtrlSubmit');

      // resolve with test response
      deferred.resolve({ test: 'data' });

      // set form to be valid
      formCtrl.$valid = true;

      // trigger click
      formBtn.triggerHandler('click');

      expect(directiveCtrl.directiveSubmit).toHaveBeenCalled();
      // how do I test that MyService and the parent function `vm.formSubmit` (which would be myFunction) gets called
    });
  });
});

Aucun commentaire:

Enregistrer un commentaire