vlio20
vlio20

Reputation: 9295

How to test if directive emited an event

I have this simple directive:

    ...
    var readyData = {
        caption: ctrl.caption,
        leftValue: ctrl.leftValue,
        rightValue: ctrl.rightValue,
    };

    $scope.$emit($attrs.id + ".ready", readyData); //$scope is directive's scope and not $rootScope
}

and I have the following test:

describe("Directive: summaryItem", function () {

  // load the directive"s module

      beforeEach(module("summaryItem"));
      it("Should emit the ready event", inject(function ($compile) {
            element = angular.element("<summary-item id=\"summaryItem\"></summary-item>");
            element = $compile(element)(scope);
            scope.$digest();
            var dscope = element.scope();
            spyOn(dscope, "$emit");

            //run code to test
            expect(dscope.$emit).toHaveBeenCalledWith("summaryItem.ready");
      }));

I am getting the following error:

Expected spy $emit to have been called with [ 'summaryItem.ready' ] but it was never called.  

How can I solve this one? Thanks!

Update
For @themyth92 request here is the full Directive's code:

"use strict";
(function (angular) {
    /**
     * @ngdoc directive
     * @name summaryItemApp.directive:summaryItem
     * @description
     * # summaryItem
     */
    angular.module("summaryItem")
        .directive("summaryItem", function () {
            return {
                templateUrl: "views/summary-item.html",
                restrict: "E",
                transclude: true,
                controller: SummaryItemCtrl,
                controllerAs: 'ctrl',
                bindToController: true,
                scope: {
                    options: "=",
                    caption: "="
                }
            };
        });

    function SummaryItemCtrl($scope, $attrs) {
        var ctrl = this;
        ctrl.caption = this.caption;
        if(this.options) {
            ctrl.leftValue = this.options.leftValue;
            ctrl.rightValue = this.options.rightValue;
        }

        var readyData = {
            caption: ctrl.caption,
            leftValue: ctrl.leftValue,
            rightValue: ctrl.rightValue
        };

        $scope.$emit($attrs.id + ".ready", readyData);
    }
}(angular)); 

Upvotes: 0

Views: 413

Answers (1)

publysher
publysher

Reputation: 11372

There are two problems in your test. First of all, the event will be triggered at the first $scope.$digest() call. In your test, you mock the $emit function after the digest, so this will not work.

Furthermore, because your directive uses an isolate scope, element.scope() does not do what you expect it to do. In this case, element.scope() will return the original scope of the element; element.isolateScope() will return the isolate scope introduced by the directive.

However, there is another way to test this. Because $emit-ted events bubble up to their parent scopes, you could also test that one of the parent scopes received the correct event.

Untested code:

  it("Should emit the ready event", inject(function ($compile) {
        var emitted = false;
        scope.$on('summaryItem.ready', function() {
          emitted = true;
        });

        element = angular.element("<summary-item id=\"summaryItem\"></summary-item>");
        element = $compile(element)(scope);
        scope.$digest();

        expect(emitted).toBe(true);
  }));

As an improvement, you can also store the event instead of just true, which allows you to do all kinds of expects on the emitted events.

Upvotes: 2

Related Questions