Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular unit testing $interval for a "clock" directive

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:

  • How do I unit test if $interval fires?
  • Why $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.

  • Testing a $interval with Jasmine in PhantomJS

A similar problem:

  • https://groups.google.com/forum/#!topic/angular/5TFvCa5m8n0

HTML

  <mydatething format="EEEE, MMMM d" interval="1000" timezone="notused"></mydatething>

DIRECTIVE

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();
      })
    }
  };
}]);

UNIT TEST

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
    });
    });

});

TERMINAL

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.

like image 621
ericjam Avatar asked Aug 13 '15 20:08

ericjam


1 Answers

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.

like image 100
resolute_coder Avatar answered Oct 09 '22 19:10

resolute_coder