Oam Psy
Oam Psy

Reputation: 8663

Unit test failing when directive is an Attribute

I have a directive which is declared as an Attribute:

app.directive('myDirective', function() {
    return {
        restrict: 'A',
        replace: true,
        transclude: true,
        scope: {
            data: "="
        },
        template:
            '<p class="my-paragrapgh">' +
                '<label>Hello</label>' +
            '</p>'
    }
});

I have a unit test, which is failing:

describe('myDirective test', function () {
var scope, compile, element;

beforeEach(module('myModule'));

beforeEach(inject(function ($rootScope, $compile) {
    scope = $rootScope.$new();

    element = angular.element("<div my-directive></div>");
    $compile(element);
    scope.$digest();
}));

it('should have a my-paragrapgh class', function () {
    expect($(element).find('p')[0]).toHaveClass('my-paragrapgh');
});

});

However, if i convert my diretive to an Element, and remove replace and transclude:

app.directive('myDirective', function() {
    return {
        restrict: 'E',
        //replace: true,
        //transclude: true,
        scope: {
            data: "="
        },
        template:
            '<p class="my-paragrapgh">' +
                '<label>Hello</label>' +
            '</p>'
    }
});

My unit test passes:

describe('myDirective test', function () {
var scope, compile, element;

beforeEach(module('myModule'));

beforeEach(inject(function ($rootScope, $compile) {
    scope = $rootScope.$new();

    element = angular.element("<my-directive></my-directive>");
    $compile(element);
    scope.$digest();
}));

it('should have a my-paragrapgh class', function () {
    expect($(element).find('p')[0]).toHaveClass('my-paragrapgh');
});

});

How can i successfully test a directive declared as an Attribute? I'm using Karma, Jasmine and PhantomJS

Upvotes: 3

Views: 1380

Answers (2)

Krzysztof Safjanowski
Krzysztof Safjanowski

Reputation: 7438

Works with replace and transclude even if there is no ng-trasclude

angular.module('myModule', [])
  .directive('myDirective', function() {
    return {
      restrict: 'A',
      replace: true,
      transclude: true,
      scope: {
        data: "="
      },
      template: '<p class="my-paragrapgh">' +
        '<label>Hello</label>' +
        '</p>'
    }
  });

describe('myDirective test', function() {
  var scope, compile, element;

  // Even we can introduce our Jasmine custom matcher
  beforeEach(function() {
    jasmine.addMatchers({
      toHaveCSSClass: function(util, customEqualityTesters) {
        return {
          compare: function(actual, expected) {
            debugger;
            var result = {};
            result.pass = util.equals(actual.hasClass(expected), true, customEqualityTesters);
            if (result.pass) {
              result.message = "Expected " + actual + " to not have CSS class '" + expected + "'";
            } else {
              result.message = "Expected " + actual + " to have CSS class '" + expected + "'";
            }
            return result;
          }
        }
      }
    });
  });

  beforeEach(module('myModule'));


  beforeEach(inject(function($rootScope, $compile) {
    scope = $rootScope.$new();
    element = angular.element("<div my-directive></div>");
    $compile(element);
    scope.$digest();
  }));

  it('has a my-paragrapgh class', function() {
    expect(element.hasClass('my-paragrapgh')).toBeTruthy();
  });
  
  it('has a my-paragrapgh class - tested by custom matcher', function() {
    expect(element).toHaveCSSClass('my-paragrapgh')
  });

});
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>

Upvotes: 0

Matt Herbstritt
Matt Herbstritt

Reputation: 4862

You will need to have an ng-transclude somewhere in your template when you have the transclude: true so that angular will know where to inject your HTML. Try:

app.directive('myDirective', function() {
    return {
        restrict: 'A',
        replace: true,
        transclude: true,

        scope: {
            data: "="
        },

        template:
            '<div ng-transclude><p class="my-paragrapgh">' +
                '<label>Hello</label>' +
            '</p></div>'
    }
});

Update

Looks like it's the replace option that could be causing the issue.

app.directive('myDirective', function() {
    return {
        restrict: 'A',

        scope: {
            data: "="
        },

        template:
            '<p class="my-paragrapgh">' +
                '<label>Hello</label>' +
            '</p>'
    }
});

With replace: true you're inner HTML is:

Fails

<label>Hello</label>

With replace undefined you have

Pass

<p class="my-paragrapgh"><label>Hello</label></p>

Upvotes: 1

Related Questions