1

I have the following angular directive, which adds a tooltip when I hover over a span.

angular.module('mainMod')
    .directive('toolTip', [function() {
        return {
            restrict: 'A',
            scope: {
                theTooltip: '@toolTip'
            },
            link: function(scope, element, attrs){
                element.tooltip({ 
                        delay: 0, 
                        showURL: false, 
                        bodyHandler: function() {
                            return jQuery('<div class="hover">').text(scope.theTooltip);
                        } 
                });
            }
        }
    }])
    ;


<span ng-show="data.tooltip" class="icon" tool-tip="{{data.tooltip}}"></span>

I'm looking to write a unit test for this directive, atm I can't use jasmine-jquery.

I'm fairly new to writing unit tests, could anyone possibly help me out?

Give me some pointers or point me towards some helpful resources?

Any advice or suggestions would be greatly appreciated.

What I have atm isn't much...

describe('Unit testing tooltip', function() {
    var $compile;
    var $rootScope;

    // Load the myApp module, which contains the directive
    beforeEach(module('mainMod'));

    // Store references to $rootScope and $compile
    // so they are available to all tests in this describe block
    beforeEach(inject(function(_$compile_, _$rootScope_){
      // The injector unwraps the underscores (_) from around the parameter names when matching
      $compile = _$compile_;
      $rootScope = _$rootScope_;
    }));


    it(' ', function() {
        // Compile a piece of HTML containing the directive
        FAILS HERE --> var element = $compile("<span class='icon' tool-tip='{{data.tooltip}}'></span>")($rootScope); 

        $rootScope.$digest();

    });
});

It's failing with a message of

TypeError: undefined is not a function

I think it's being caused by the ($rootScope) at the end of the line I've specified above.

Daft
  • 10,277
  • 15
  • 63
  • 105

2 Answers2

2

You have to wrap your DOM content with angular.element first before compiling it. I am not sure what the tooltip module you are using but I used the jQuery UI tooltip instead.

    //create a new scope with $rootScope if you want
    $scope  = $rootScope.$new();

    var element = angular.element("<span class='icon' tool-tip='This is the tooltip data'></span>");

    //use the current scope has just been created above for the directive
    $compile(element)($scope);

One more thing, because you are using isolate scope in your directive, to get the current scope from your directive, you need to call

   element.isolateScope()

based on this reference : How to Unit Test Isolated Scope Directive in AngularJS

For a working fiddle, you can found it here : http://jsfiddle.net/themyth92/4w52wsms/1/

Community
  • 1
  • 1
themyth92
  • 1,743
  • 2
  • 17
  • 23
  • According to https://docs.angularjs.org/api/ng/service/$compile, you don't have to wrap anything, a string template is just fine. Also, there's no need to worry about getting the isolated scope when testing the unit from the outside. – hon2a Nov 21 '14 at 07:01
  • Have you tried without using the angular.element, because when I remove that part, it will cause error. Maybe it relates to angular version. – themyth92 Nov 21 '14 at 07:20
  • I use `$compile` to compile string templates in all directive tests across a wide array of AngularJS versions without any problems. – hon2a Nov 21 '14 at 07:28
  • Can you please update my fiddle with $compile without angular element ? It will be great :) – themyth92 Nov 21 '14 at 07:35
  • Wow, thanks alot. How about the isolate scope part, any ways to get the directive scope ? – themyth92 Nov 21 '14 at 07:47
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/65340/discussion-between-hon2a-and-themyth92). – hon2a Nov 21 '14 at 07:51
-1

Any unit test is basically the same - mock the environment, construct the unit, check that the unit interacts with the environment in the expected manner. In this instance, you'd probably want to mock the tooltip creator

spyOn(jQuery.fn, 'tooltip');

then compile some template using the directive (which you're already doing), then simulate the hover event on the compiled element and then check that the tooltip creator was called in the expected manner

expect(jQuery.fn.tooltip).toHaveBeenCalledWith(jasmine.objectContaining({
    // ... (expected properties)
}));

How you simulate the event depends on how the element.tooltip is supposed to work. If it really works the way you're using it in the question code, you don't need to simulate anything at all and just check the expected interaction right after template compilation.

hon2a
  • 7,006
  • 5
  • 41
  • 55
  • Thanks a lot hon2a. It really does work how I've described it in the question. I think I have a decent understanding of what steps to take, but the test is failing on me in the line I've highlighted above, and I'm not sure why. – Daft Nov 20 '14 at 16:47
  • @Daft Then you need to do a little debugging and find out exactly, what is failing. From here, without knowing anything about `tooltip`, I can see just 3 possible things that might not exist - `.tooltip` plugin, `jQuery` function, and `.text` function. Find out, which one is it, and act accordingly. – hon2a Nov 21 '14 at 07:09
  • @Daft On a sidenote, you really shouldn't use `jQuery('
    ')` to create an element. Either use `jQuery('
    ').addClass('hover')` (which creates element by tag name) or `jQuery('
    ')` (proper HTML fragment).
    – hon2a Nov 21 '14 at 07:11