Neil Neil - 2 months ago 18
AngularJS Question

How to mock $window.Notification

I am still learning the ropes when it comes to unit testing with angular. I have an angular service that I use to create HTML5 notifications. Code is similar to the following:

(function() {
'use strict';

angular
.module('blah')
.factory('OffPageNotification', offPageNotificationFactory);

function offPageNotificationFactory($window) {
//Request permission for HTML5 notifications as soon as we can
if($window.Notification && $window.Notification.permission !== 'denied') {
$window.Notification.requestPermission(function (status) { });
}

function OffPageNotification () {
var self = Object.create(OffPageNotification.prototype);
self.visibleNotification = null;
return self;
}


OffPageNotification.prototype.startNotification = function (options) {
var self = this;
self.options = options;
if(self.options.showHtml5Notification && (!self.options.onlyShowIfPageIsHidden || $window.document.hidden)) {
if($window.Notification && $window.Notification.permission !== 'denied') {
self.visibleNotification = new $window.Notification('Notification', {
body: self.options.notificationText,
icon: self.options.notificationIcon
});
}
}
};

.
.
.
return new OffPageNotification();
}
})();


I am attempting to write unit tests for this but am unsure how to mock $window.Notification so it can be used as both a constructor...

self.visibleNotification = new $window.Notification(....)


and also contain properties

if($window.Notification && $window.Notification.permission !== 'denied')


and methods....

$window.Notification.requestPermission(


An example of something I have tried is:

describe('startNotification', function() {

beforeEach(function() {
var mockNotification = function (title, options) {
this.title = title;
this.options = options;
this.requestPermission = sinon.stub();
};

mockNotification.prototype.permission = 'granted';

mockWindow = {
Notification: new mockNotification('blah', {}),
document: {hidden: true}
};

inject(function (_OffPageNotification_) {
OffPageNotification = _OffPageNotification_;
});
});

it('should display a html5 notification if the relevant value is true in the options, and permission has been granted', function(){
var options = {
showHtml5Notification: true,
onlyShowIfPageIsHidden: true
};
OffPageNotification.startNotification(options);
});
});


I get an error saying '$window.Notification is not a constructor' with this setup and I understand why (I am passing in an instantiated version of the mockNotification). But if I set mockWindow.Notification = mockNotification then I get an error when it calls requestPermission since this is undefined.

Any help is appreciated

Answer

Notification should be a constructor. And it should have static properties and methods.

All of the relevant properties of mockNotification are instance properties, while they should be static:

function MockNotification() {}

MockNotification.title = title;
MockNotification.options = options;
MockNotification.requestPermission = sinon.stub();

   mockWindow = {
     Notification: MockNotification,
     document: {hidden: true}
   };
Comments