session-timeout
Version:
session timeout
605 lines (425 loc) • 18.2 kB
JavaScript
describe('ngIdle', function() {
// helpers
beforeEach(function() {
this.addMatchers({
toEqualData: function(expected) {
return angular.equals(this.actual, expected);
}
});
});
describe('idle', function() {
var IdleProvider, $interval, $rootScope, $log, $document, Keepalive, $injector, LocalStorage;
var DEFAULTIDLEDURATION = 20*60*1000, DEFAULTTIMEOUT = 30 * 1000;
beforeEach(module('ngIdle.idle'));
beforeEach(function() {
angular.module('app', []).config(['IdleProvider',
function(_IdleProvider_) {
IdleProvider = _IdleProvider_;
}
]);
module('app');
inject(function(_$interval_, _$log_, _$rootScope_, _$document_, _$injector_, _IdleLocalStorage_) {
$rootScope = _$rootScope_;
$interval = _$interval_;
$log = _$log_;
$document = _$document_;
$injector = _$injector_;
LocalStorage = _IdleLocalStorage_;
});
Keepalive = {
start: function() {},
stop: function() {},
ping: function() {}
};
spyOn(Keepalive, 'start');
spyOn(Keepalive, 'stop');
spyOn(Keepalive, 'ping');
});
var create = function(keepalive) {
if (angular.isDefined(keepalive)) IdleProvider.keepalive(keepalive);
return $injector.invoke(IdleProvider.$get, null, {$interval: $interval, $log: $log, $rootScope: $rootScope, $document: $document, Keepalive: Keepalive, LocalStorage:LocalStorage});
};
describe('IdleProvider', function() {
it('interrupt() should update defaults', function() {
expect(IdleProvider).not.toBeUndefined();
IdleProvider.interrupt('click');
expect(create()._options().interrupt).toBe('click');
});
it('windowInterrupt() should update defaults', function() {
expect(IdleProvider).not.toBeUndefined();
IdleProvider.windowInterrupt('focus');
expect(create()._options().windowInterrupt).toBe('focus');
});
it('idle() should update defaults', function() {
expect(IdleProvider).not.toBeUndefined();
IdleProvider.idle(500);
expect(create()._options().idle).toBe(500);
});
it('idle() should throw if argument is less than or equal to zero.', function() {
var expected = new Error('Idle must be a value in seconds, greater than 0.');
expect(function() {
IdleProvider.idle(0);
}).toThrow(expected);
expect(function() {
IdleProvider.idle(-1);
}).toThrow(expected);
});
it('autoResume() should interpret false as off', function() {
expect(IdleProvider).not.toBeUndefined();
IdleProvider.autoResume(false);
expect(create()._options().autoResume).toBe('off');
});
it('autoResume() should interpret true as idle', function() {
expect(IdleProvider).not.toBeUndefined();
IdleProvider.autoResume(true);
expect(create()._options().autoResume).toBe('idle');
});
it('keepalive() should update defaults', function() {
IdleProvider.keepalive(false);
expect(create()._options().keepalive).toBe(false);
});
it ('setting timeout() with false should set timeout to 0', function() {
expect(IdleProvider).not.toBeUndefined();
IdleProvider.timeout(false);
expect(create()._options().timeout).toBe(0);
});
it ('setting timeout() with 0 should set timeout to 0', function() {
expect(IdleProvider).not.toBeUndefined();
IdleProvider.timeout(0);
expect(create()._options().timeout).toBe(0);
});
it ('setting timeout() with should throw an error if NaN', function() {
expect(IdleProvider).not.toBeUndefined();
expect(function() {
IdleProvider.timeout('hello');
}).toThrow(new Error('Timeout must be zero or false to disable the feature, or a positive integer (in seconds) to enable it.'));
});
it ('setting timeout() with with positive integer should set timeout', function() {
expect(IdleProvider).not.toBeUndefined();
IdleProvider.timeout(999);
expect(create()._options().timeout).toBe(999);
});
});
describe('Idle', function() {
var Idle;
beforeEach(function() {
LocalStorage.remove('expiry');
IdleProvider.timeout(3);
Idle = create();
});
it ('setIdle() should update option.idle and restart', function() {
spyOn(Idle, 'watch');
spyOn(Idle, 'unwatch');
spyOn(Idle, 'running').andCallFake(function() {return true;});
Idle.setIdle(100);
expect(Idle._options().idle).toBe(100);
expect(Idle.unwatch).toHaveBeenCalled();
expect(Idle.watch).toHaveBeenCalled();
});
it ('getIdle() should return the current idle value', function(){
Idle.setIdle(100);
expect(Idle.getIdle()).toBe(100);
});
it ('getTimeout() should return the current timeout value', function(){
Idle.setTimeout(100);
expect(Idle.getTimeout()).toBe(100);
});
it ('setTimeout() should update option.timeout and restart', function() {
spyOn(Idle, 'watch');
spyOn(Idle, 'unwatch');
spyOn(Idle, 'running').andCallFake(function() {return true;});
Idle.setTimeout(100);
expect(Idle._options().timeout).toBe(100);
expect(Idle.unwatch).toHaveBeenCalled();
expect(Idle.watch).toHaveBeenCalled();
});
it('watch() should clear timeouts and start running', function() {
spyOn($interval, 'cancel');
Idle.watch();
expect($interval.cancel).toHaveBeenCalled();
expect(Idle.running()).toBe(true);
expect(Keepalive.start).toHaveBeenCalled();
});
it('watch() should not start keepalive if disabled', function() {
Idle = create(false);
Idle.watch();
expect(Keepalive.start).not.toHaveBeenCalled();
});
it('should not stop keepalive when idle if keepalive integration is disabled', function() {
Idle = create(false);
Idle.watch();
$interval.flush(DEFAULTIDLEDURATION);
expect(Keepalive.stop).not.toHaveBeenCalled();
});
it('should not start or ping keepalive when returning from idle if integration is disabled', function() {
Idle = create(false);
Idle.watch();
$interval.flush(DEFAULTIDLEDURATION);
Idle.watch();
expect(Keepalive.ping).not.toHaveBeenCalled();
expect(Keepalive.start).not.toHaveBeenCalled();
});
it('unwatch() should clear timeouts and stop running', function() {
Idle.watch();
spyOn($interval, 'cancel');
Idle.unwatch();
expect($interval.cancel).toHaveBeenCalled();
expect(Idle.running()).toBe(false);
});
it('unwatch() should stop keepalive if enabled', function() {
Idle.watch();
Idle.unwatch();
expect(Keepalive.stop).toHaveBeenCalled();
});
it('should broadcast IdleStart and stop keepalive', function() {
spyOn($rootScope, '$broadcast');
Idle.watch();
$interval.flush(DEFAULTIDLEDURATION);
$rootScope.$digest();
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleStart');
expect(Keepalive.stop).toHaveBeenCalled();
});
it('should broadcast IdleStart then IdleWarn', function() {
spyOn($rootScope, '$broadcast');
Idle.watch();
$interval.flush(DEFAULTIDLEDURATION);
$rootScope.$digest();
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleStart');
expect($rootScope.$broadcast.mostRecentCall.args[0]).toBe('IdleWarn');
});
it('should broadcast IdleEnd, start keepalive and ping', function() {
spyOn($rootScope, '$broadcast');
Idle.watch();
$interval.flush(DEFAULTIDLEDURATION);
$rootScope.$digest();
Idle.watch();
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleEnd');
expect(Keepalive.ping).toHaveBeenCalled();
expect(Keepalive.start).toHaveBeenCalled();
});
it('should count down warning and then signal timeout', function() {
spyOn($rootScope, '$broadcast');
Idle.watch();
$interval.flush(DEFAULTIDLEDURATION);
$rootScope.$digest();
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleStart');
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleWarn', 3);
$interval.flush(1000);
$rootScope.$digest();
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleWarn', 2);
$interval.flush(1000);
$rootScope.$digest();
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleWarn', 1);
$interval.flush(1000);
$rootScope.$digest();
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleTimeout');
// ensure idle interval doesn't keep executing after IdleStart
$rootScope.$broadcast.reset();
$interval.flush(DEFAULTIDLEDURATION);
$interval.flush(DEFAULTIDLEDURATION);
expect($rootScope.$broadcast).not.toHaveBeenCalledWith('IdleStart');
});
it('watch() should interrupt countdown', function() {
spyOn($rootScope, '$broadcast');
Idle.watch();
$interval.flush(DEFAULTIDLEDURATION);
$interval.flush(1000);
$rootScope.$digest();
expect(Idle.idling()).toBe(true);
Idle.watch();
expect(Idle.idling()).toBe(false);
});
it ('isExpired() should return false if the date/time is less than the idle duration', function() {
// sets the expiry to now + idle + warning duration
Idle.watch();
expect(Idle.isExpired()).toBe(false);
});
it ('isExpired() should return true if the date/time is greater than or equal the idle duration + warning duration.', function() {
var secondsPassed = 0;
// fake now to return a time in the future.
spyOn(Idle, '_getNow').andCallFake(function() {
return new Date(new Date().getTime() + ((DEFAULTIDLEDURATION + DEFAULTTIMEOUT + secondsPassed) * 1000));
});
// equal to expiry
Idle.watch();
expect(Idle.isExpired()).toBe(true);
// greater than expiry
secondsPassed = 1;
Idle.watch();
expect(Idle.isExpired()).toBe(true);
// far greater than expiry (90 days)
secondsPassed = 60 * 60 * 24 * 90;
Idle.watch();
expect(Idle.isExpired()).toBe(true);
});
it ('isExpired() should return false if there is no expiry yet', function() {
expect(Idle.isExpired()).toBe(false);
});
it ('interrupt() should broadcast $timeout if running and past expiry', function() {
spyOn($rootScope, '$broadcast');
// fake now to return a time in the future.
spyOn(Idle, '_getNow').andCallFake(function() {
return new Date(new Date().getTime() + ((DEFAULTIDLEDURATION + DEFAULTTIMEOUT + 60) * 1000));
});
spyOn(Idle, 'watch').andCallThrough();
// the original call to start watching
Idle.watch();
expect($rootScope.$broadcast).not.toHaveBeenCalled();
Idle.watch.reset();
// a subsequent call represents an interrupt
Idle.interrupt();
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleTimeout');
expect(Idle.idling()).toBe(true);
expect(Idle.watch).not.toHaveBeenCalled();
});
it ('interrupt(true) should not update expiry', function() {
Idle.watch();
spyOn(LocalStorage, 'set').andCallThrough();
Idle.interrupt(true);
expect(LocalStorage.set).not.toHaveBeenCalled();
});
it ('interrupt() should update expiry', function() {
Idle.watch();
spyOn(LocalStorage, 'set').andCallThrough();
Idle.interrupt();
expect(LocalStorage.set).toHaveBeenCalled();
});
it ('interrupt() should broadcast IdleInterrupt if user has not timed out', function() {
spyOn($rootScope, '$broadcast');
Idle.watch();
Idle.interrupt();
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleInterrupt', undefined);
});
it ('interrupt() should broadcast IdleInterrupt from another tab', function() {
spyOn($rootScope, '$broadcast');
Idle.watch();
Idle.interrupt(true);
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleInterrupt', true);
});
it ('interrupt() should not broadcast IdleInterrupt if user has timed out', function() {
spyOn($rootScope, '$broadcast');
// fake now to return a time in the future.
spyOn(Idle, '_getNow').andCallFake(function() {
return new Date(new Date().getTime() + ((DEFAULTIDLEDURATION + DEFAULTTIMEOUT + 60) * 1000));
});
Idle.watch();
Idle.interrupt();
expect($rootScope.$broadcast).not.toHaveBeenCalledWith('IdleInterrupt');
});
// HACK: the body event listener is only respected the first time, and thus always checks the first Idle instance we created rather than the one we created last.
// in practice, the functionality works fine, but here the test always fails. dunno how to fix it right now.
// it ('document event should interrupt idle timeout', function() {
// Idle.watch();
// $timeout.flush();
// expect(Idle.idling()).toBe(true);
// var e = $.Event('click');
// $('body').trigger(e);
// expect(Idle.idling()).toBe(false);
// });
//HACK: this has the same issues as the test above
//it('window event should interrupt idle timeout', function() {
// IdleProvider.windowInterrupt('focus');
// Idle = create();
//
// Idle.watch();
//
//});
});
describe('Idle with different autoResume values', function() {
var Idle;
it('interrupt() should NOT call watch() if autoResume is off', function() {
IdleProvider.autoResume('off');
Idle = create();
spyOn(Idle, 'watch').andCallThrough();
Idle.watch();
Idle.watch.reset();
Idle.interrupt();
expect(Idle.watch).not.toHaveBeenCalled();
});
it ('interrupt() should call watch() if running and autoResume is idle', function() {
IdleProvider.autoResume('idle');
Idle = create();
spyOn(Idle, 'watch').andCallThrough();
// arrange
Idle.watch(); // start watching
Idle.watch.reset(); // reset watch spy to ignore the prior setup call
Idle.interrupt();
expect(Idle.watch).toHaveBeenCalled();
});
it ('interrupt() should call watch() if not idle and autoResume is notIdle', function() {
IdleProvider.autoResume('notIdle');
Idle = create();
spyOn(Idle, 'watch').andCallThrough();
// arrange
Idle.watch(); // start watching
Idle.watch.reset(); // reset watch spy to ignore the prior setup call
Idle.interrupt();
expect(Idle.watch).toHaveBeenCalled();
});
it ('interrupt() should NOT call watch() if idle and autoResume is notIdle', function() {
IdleProvider.autoResume('notIdle');
Idle = create();
spyOn(Idle, 'watch').andCallThrough();
// arrange
Idle.watch(); // start watching
Idle.watch.reset(); // reset watch spy to ignore the prior setup call
$interval.flush(DEFAULTIDLEDURATION);
Idle.interrupt();
expect(Idle.watch).not.toHaveBeenCalled();
});
it ('interrupt(true) should call watch() if idle and autoResume is notIdle', function() {
IdleProvider.autoResume('notIdle');
Idle = create();
spyOn(Idle, 'watch').andCallThrough();
// arrange
Idle.watch(); // start watching
Idle.watch.reset(); // reset watch spy to ignore the prior setup call
$interval.flush(DEFAULTIDLEDURATION);
Idle.interrupt(true);
expect(Idle.watch).toHaveBeenCalled();
});
});
describe('Idle with timeout disabled', function() {
var Idle;
beforeEach(function() {
IdleProvider.timeout(false);
Idle = create();
});
it('should NOT count down warning or signal timeout', function() {
spyOn($rootScope, '$broadcast');
Idle.watch();
$interval.flush(DEFAULTIDLEDURATION);
$rootScope.$digest();
expect($rootScope.$broadcast).toHaveBeenCalledWith('IdleStart');
expect($rootScope.$broadcast).not.toHaveBeenCalledWith('IdleWarn');
$interval.flush(1000);
$rootScope.$digest();
expect($rootScope.$broadcast).not.toHaveBeenCalledWith('IdleWarn');
$interval.flush(1000);
$rootScope.$digest();
expect($rootScope.$broadcast).not.toHaveBeenCalledWith('IdleWarn');
$interval.flush(1000);
$rootScope.$digest();
expect($rootScope.$broadcast).not.toHaveBeenCalledWith('IdleTimeout');
});
it ('interrupt() should not timeout if running and past expiry', function() {
spyOn($rootScope, '$broadcast');
// fake now to return a time in the future.
spyOn(Idle, '_getNow').andCallFake(function() {
return new Date(new Date().getTime() + ((DEFAULTIDLEDURATION + DEFAULTTIMEOUT + 60) * 1000));
});
spyOn(Idle, 'watch').andCallThrough();
// the original call to start watching
Idle.watch();
expect($rootScope.$broadcast).not.toHaveBeenCalled();
Idle.watch.reset();
// a subsequent call represents an interrupt
Idle.interrupt();
expect($rootScope.$broadcast).not.toHaveBeenCalledWith('IdleTimeout');
expect(Idle.idling()).toBe(false);
expect(Idle.watch).toHaveBeenCalled();
});
});
});
});
;