0

I have a super simple directive that overrides click behavior and does a full page reload on click. I'm having trouble writing a Unit Test for this directive. It looks like the $window doesn't get injected properly as well as this error when running the test:

TypeError: 'undefined' is not an object (evaluating '$event.preventDefault')

reload.directive.js

 angular.module('myModule')
    .directive('reload', ['$window', function($window) {
      return {
        restrict: 'A',
        scope: {},
        transclude: true,
        replace: true,
        template: '<a ng-click="reload($event)" ng-transclude></a>',
        link: function(scope, element, attrs) {
          scope.reload = function($event) {
            $event.preventDefault();
            $window.location.href = attrs.href;
          };
        }
      };
    }]);

An example of how I'm using it

<a ui-sref="home", reload>Home Example</a>

Here is my unit test: reload-test.directive.js

describe('Testing reload directive', function() {
  beforeEach(module('myModule'));

  var window, element, scope;

  beforeEach(inject(function($compile, $rootScope, $window) {
    scope = $rootScope.$new();
    window = $window;
    element = $compile('<a reload href="/"></a>')(scope);
    scope.$digest();
  }));

  it('should reload the page with the right url', function() {
    var compiledElementScope = element.isolateScope();
    compiledElementScope.reload();
    expect(window.location.href).toEqual('/');
  });
});

UPDATED

Instead of doing any of this, I can just use target="_self" on links which triggers a full page reload.

cusejuice
  • 10,285
  • 26
  • 90
  • 145
  • Note, that you might no need to use this directive. if you want to trigger page reload on links in angular app, just add `target="_self"` attribute to them. This answer http://stackoverflow.com/a/11857881/1297743 still actual – just-boris Jun 11 '15 at 21:05
  • Amazing. Good catch. – cusejuice Jun 11 '15 at 21:17

1 Answers1

0

Your test would be more natural if you will trigger an event.

element.triggerHandler('click');

Then your handler will be called by internal angular mechanisms.

Also your test will be failed when you trying to update window.location, because it causes full page reload. So, you need to mock window here:

var fakeWindow, element, scope;
beforeEach(module('myModule'));
beforeEach(function() {
    // define fake instance for $window
    module(function($provide) {
       fakeWindow = {location: {}};
       $provide.value('$window', fakeWindow)
    });
});

beforeEach(inject(function($compile, $rootScope) {
    scope = $rootScope.$new();
    element = $compile('<a reload href="/"></a>')(scope);
    scope.$digest();
}));

it('should reload the page with the right url', function() {
   var event = jasmine.createSpyObj('clickEvent', ['preventDefault']);
   event.type = 'click';
   element.triggerHandler(event)
   expect(fakeWindow.location.href).toEqual('/');
   expect(event.preventDefault).toHaveBeenCalled();
});

Now you can safely test your behaviour without side-effects.

just-boris
  • 9,468
  • 5
  • 48
  • 84
  • Thanks. So the link function would basically look like: `element.on('click', function(e){ e.preventDefault(); $window.location.href = attrs.href; }); And the template would look like `` ?? How can I test that e.preventDefault() was called? – cusejuice Jun 11 '15 at 21:14
  • No, you still can use `ng-click`, and angular will create a event handler for you. See updated answer with `preventDefault` call tracking via `jasmine.createSpyObj` – just-boris Jun 11 '15 at 22:06
  • Thanks for the tip. Just noticed @just-boris that `click.type = 'click';` should be `event.type = 'click';` for this to work for me. –  Jun 13 '15 at 13:42
  • Sorry, that was a typo. Updated the answer – just-boris Jun 13 '15 at 19:24