David Barker
David Barker

Reputation: 14620

Unit testing an angular directive similar to ng-if

I have a service and a directive packaged together that remove DOM elements if certain criteria are not met.

The service performs a check against a string passed in and the directive simply removes the DOM element it's attached to if the check fails.

This works in IE11, Edge, Chrome and FireFox. However, I can't seem to replicate this in any tests.

Working code in this JSBin

Here's an example of the service and its provider

class PermissionService {
    constructor(permissions) {
        this.permissions = permissions;
    }

    addPermissions(permissions) {
        this.permissions = this.permissions.concat(permissions);
    }

    has(permission) {
        return this.permissions.indexOf(permission) !== -1;
    }
}

class PermissionProvider {
    constructor() {
        let _permissions = [];

        this.setPermissions = (permissions) => {
            _permissions = _permissions.concat(permissions);
        };

        this.$get = () => {
            return new PermissionService(_permissions);
        }
    }
}

And the directive with an example of how it is setup in an angular module.

angular.module('PermissionTest', [])
  .directive('hasPermission', ['permission', '$compile', (service, $compile) => {
    return {
      restrict: 'A',
      priority: 1,
      terminal: true,
      compile: (element, attrs) => {
        let permission = attrs.hasPermission;

        if (service.has(permission)) {
            attrs.$set('hasPermission', null);
            return (scope, element) => {
                $compile(element)(scope);
            }
        } else {
            element.remove();
        }
      }
    }
  }])
  .provider('permission', PermissionProvider)
  .config(['permissionProvider', (provider) => {
    provider.setPermissions(['yes']);
  }]);

Using the directive, I can now remove elements from the DOM easily

<!-- Is not removed -->
<div has-permission="yes">Is not removed</div>

<!-- Is removed -->
<div has-permission="no">Is removed</div>

I have the following test setup that I would expect to pass, but does not. I suspect it is due to the directive recompiling itself, though I think this should occur in the scope.$digest(). Any help debugging this would be helpful.

let provider;
let service;

describe('directive: has-permission', () => {
    beforeEach(module('permissionChecker', (permissionProvider) => {
        provider = permissionProvider;
        provider.setPermissions(['crm.check.r', 'crm.check.c']);
    }));

    beforeEach(inject((permission) => {
        service = permission;
    }));
});

describe('if directive is not valid', () => {
    let scope;
    let element;

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

        element = `<div>
            <div has-permission="crm.not.r"></div>
            <div has-permission="crm.check.r"></div>
        </div>`;

        element = $compile(element)(scope);

        scope.$digest();
    }));

    it('should remove one child element', () => {
        let test = angular.element(element).children();
        expect(test.length).toBe(1);
    });
});

Upvotes: 0

Views: 428

Answers (1)

David Barker
David Barker

Reputation: 14620

As is always the way, minutes after posting this question I found the solution to the problem. My assumption that I could build the provider in a separate describe from the remainder of the tests was incorrect.

I debugged this by add the following expectation to the first describe:

describe('directive:has-permission', () => {
    // Provider set up in question

    it('should exist', inject(($injector) => {
        expect($injector.has('hasPermissionDirective')).toBe(true);
    }));
});

This passed successfully.

I then added the same expectation to the following describe closures and to my surprise the test failed.

To solve this I nested all the subsequent describes inside the provider setup and all works as expected.

describe('directive:has-permission', () => {

    // Provider setup

    describe('if directive is not valid', () => {
        // Test setup and code
    });
});

Upvotes: 1

Related Questions