Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing element directive - can't access isolated scope methods during tests

I have the following directive.

directivesModule.directive('wikis', function() {
var urlRegex = new RegExp('^(https?)://.+$');

return {
    restrict: 'E', 
    templateUrl: 'templates/wiki-list.html',
    scope: {
        wikis: '='
    },
    link: function(scope, element, attrs) {
        scope.newWikiURL = '';

        scope.$watch('wikis', function() {
            if (scope.wikis == undefined || scope.wikis.length === 0) {
                scope.class = 'hide';
            } else {
                scope.class = 'show';
            }
        }, false);

        scope.addWiki = function() {
            if (scope.wikis == undefined) {
                scope.wikis = [];
            }

            var nw = scope.newWikiURL;
            if (nw != undefined && nw.trim() != '' && urlRegex.exec(nw)) { 
                scope.wikis.push(nw);
                scope.newWikiURL = '';
            }
        }

    }
};

});

When I test it:

describe('Wikis Directive Test Suite', function() {
    var scope, elem, directive, linkFn, html;

    beforeEach(function() {
        html = '<wikis wikis=''></wikis>';

        inject(function($compile, $rootScope) {
            scope = $rootScope.$new();
            scope.wikis = [];

            elem = angular.element(html);

            $compile(elem)(scope);

            scope.$digest();
        });

    });

    it('add Wiki should add a valid wiki URL to artist', function() {
        var url = 'http://www.foo.com';
        scope.newWikiURL = url;
        scope.addWiki();

        expect(scope.wikis.length).toBe(1);
        expect(scope.wikis[0]).toBe(url);
        expect(scope.newWikiURL).toBe('');
    });
});

I get an error saying that Object doesn't have an addWiki method. I tried to debug it, and the link function is not called during the test. I suspect that's why the addWiki method is not part of the scope. Anybody knows why I'm getting this error?

By the way, Is it a normal practice to add some logic into the link function of a directive as it would be a Controller itself? Because looking at the code I know that it's why in reality I'm doing.

like image 887
leonfs Avatar asked Sep 10 '13 08:09

leonfs


People also ask

What is restrict option in directive?

Directive comes with many Directive Definition Objects (DDO). From them restrict is one. Using restrict option inside a Custom Directive we can prevent the access level of Directive for Element(E), Attribute(A), Comment(M) or Class(C).

What is attrs in AngularJS?

Using attrs you are able to access the attributes defined in your html tag like <fm-rating ng-model="$parent.restaurant.price" symbol="$" readonly="true"> So in this case you will have access to the symbol and readonly attributes.

What is the number of scopes allowed in an AngularJS application?

Each AngularJS application has exactly one root scope, but may have any number of child scopes.


2 Answers

As per angular 1.2.0 docs, the way to get the isolate scope is through the method isolateScope

  • scope() - retrieves the scope of the current element or its parent.

  • isolateScope() - retrieves an isolate scope if one is attached directly to the current element. This getter should be used only on elements that contain a directive which starts a new isolate scope. Calling scope() on this element always returns the original non-isolate scope.

Angular doc - section jQuery/jqLite Extras

BREAKING CHANGE: jqLite#scope()

like image 184
yeraycaballero Avatar answered Oct 05 '22 07:10

yeraycaballero


  1. You need to load the module containing your directive, otherwise angular doesn't know what <wikis> is

  2. Your directive creates an isolate scope, so once it has been compiled you need to get the new scope using elem.isolateScope()

So with those changes:

describe('Wikis Directive Test Suite', function() {
    var $scope, scope, elem, directive, linkFn, html;

    beforeEach(module('app'));

    beforeEach(function() {
        html = '<wikis></wikis>';

        inject(function($compile, $rootScope, $templateCache) {
            $templateCache.put('templates/wiki-list.html', '<div>wiki template</div>');

            $scope = $rootScope.$new();
            $scope.wikis = [];

            elem = angular.element(html);

            $compile(elem)($scope);

            scope = elem.isolateScope();
            scope.$apply();
        });

    });

    it('add Wiki should add a valid wiki URL to artist', function() {
        var url = 'http://www.foo.com';
        scope.newWikiURL = url;
        scope.addWiki();

        expect(scope.wikis.length).toBe(1);
        expect(scope.wikis[0]).toBe(url);
        expect(scope.newWikiURL).toBe('');
    });
});

http://jsfiddle.net/QGmCF/1/

like image 26
noj Avatar answered Oct 05 '22 05:10

noj