I have an Angular directive "clock" and I'm trying to write a unit test to see if the clock actually $interval advances to a future time (ie: 2 minutes by looking at element.text()
). I have a passing test of the current time, now I want to test if it will show a future time via $interval.flush
. It appears to me $interval.flush
isn't really advancing the clock.
Can I ask for two answers:
$interval
fires?$interval.flush
doesn't seem to advance the Date()
?I'm following guidelines from these posts:
how to unit-test setInterval in karma angularjs
http://www.bradoncode.com/blog/2015/06/15/unit-testing-interval-angularls/
A related post suggested using Jasmine mocks, which I don't think is necessary anymore.
A similar problem:
<mydatething format="EEEE, MMMM d" interval="1000" timezone="notused"></mydatething>
myApp.directive('mydatething', ['$interval', 'dateFilter', function ($interval, dateFilter) {
return {
restrict: "AE",
scope: {
format: '@',
interval: '@'
},
template: '', // the template is the Date() output
link: function (scope, element, attrs) {
// scope expects format, interval and timezone
var clockid;
var clockinterval = scope.interval;
var dateformat = scope.format;
var clocktimezone = scope.timezone;
// DOM update function
function updateClock() {
element.text(dateFilter(new Date(), dateformat));
}
// Instantiate clock
updateClock();
clockid = $interval(updateClock, clockinterval); // fixed
// For cancelling
scope.$on('$destroy', function () {
$interval.cancel(clockid);
});
// Separate listener for locale change, manually refresh clock format
scope.$on('$localeChangeSuccess', function () {
updateClock();
})
}
};
}]);
describe("tsdate directive", function(){
var elem, scope, $interval, dateFilter;
beforeEach(module('tsApp'));
beforeEach(inject(function(_$rootScope_, _$interval_, _$compile_, _dateFilter_){
$compile = _$compile_;
dateFilter = _dateFilter_;
$interval = _$interval_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
elem = angular.element('<mydatething format="h:mm a" interval="15000"></mydatething>');
elem = $compile(elem)(scope);
scope.$digest();
}));
describe('on clock start', function() {
it('to show the current date', function() {
var currentdate = dateFilter(new Date(), elem.isolateScope().format);
expect(elem.text()).toBe(currentdate);
// this passes
});
it('that it updates the clock', function() {
var futurems = 120000; // 2 minutes
var futuredate = dateFilter(new Date().getTime() + futurems, elem.isolateScope().format)
$interval.flush(futurems);
expect(elem.text()).toBe(futuredate);
// this fails
});
});
});
PhantomJS 1.9.8 (Mac OS X) mydatething directive on clock start that it updates the clock FAILED
Expected '3:55' to be '3:57'.
Console.log
reveals, the futuredate
var is 2 minutes incremented, but that the elem.text()
remains the current time.
NOTE BEFORE I START:
You have an error in your directive code. When calling $interval, you pass the function object as the first parameter. No parenthesis.
// Do this
clockid = $interval(updateClock, clockinterval);
// Not this
clockid = $interval(updateClock(), clockinterval);
See the difference on plunker
Secondly, calling $interval.flush causes interval to move forward by the number of milliseconds, but it has not effect on the internal Javascript clock. Since you are using Date to update the time on the clock, you are always getting the current time. Calling $interval.flush may cause interval to update the clock a bunch of times, but it's always setting it to the current time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With