7

I've made a directive which is used to show notification messages to a user. To show the notification I wrote this:

$scope.$watch($messaging.isUpdated, function() {
    $scope.messages = $messaging.getMessages();
    if ($scope.messages.length > 0) {
        $timeout(function() {
            for (var i = 0; i < $scope.messages.length; i++) {
                if (i + 1 < $scope.messages.length) {
                    $messaging.removeMessage($scope.messages[i]);
                } else {
                    $messaging.removeMessage($scope.messages[i]);
                }
            }
        }, 5000);
    }
});

I'm using $timeout to make sure the messages stays on the screen for 5 seconds.

Now I want to write End-To-End test on it so I can be sure the notification is shown. Problem is when the notification is shown the End-To-End is also timed out just like the notification message. This makes it impossible to check whether the right notification is shown. .

This is my test code:

it('submit update Center', function() {
    input('center.Name').enter('New Name');
    input('center.Department').enter('New Department');
    input('center.Contact').enter('New contact');
    input('center.Street').enter('New street');
    input('center.City').enter('New city');
    input('center.Country').enter('New Country');
    element('button#center_button').click();

    expect(element('.feedback').count()).toBe(1);
    expect(element('.feedback:first').attr('class')).toMatch(/success/);

    expect(element('.error.tooltip').count()).toBe(0);
});

I’d like to avoid using the javascript setTimeout() and hope there’s another (Angular) solution to this problem.

Jeffrey Rosselle
  • 745
  • 1
  • 9
  • 25
  • 1
    Can you make this a unit test instead, mocking out `$timeout` and verifying that it was called with the right arguments? – Nick Heiner Apr 19 '13 at 21:28

2 Answers2

11

Bad news, pal. It's a known issue in AngularJs. There is discussion here and a "somehow related" issue here.

Fortunately, you can workaround this by rolling up your own $timeout service, calling setTimeout and calling $apply by hand (this is a suggestion from the discussion I've referred). It's really simple, although it's really ugly. A simple example:

app.service('myTimeout', function($rootScope) {
  return function(fn, delay) {
    return setTimeout(function() {
      fn();
      $rootScope.$apply();
    }, delay);
  };
});

Note this one is not compatible with Angular $timeout, but you can extend your functionality if you need.

Caio Cunha
  • 23,326
  • 6
  • 78
  • 74
0

tiny note about you if/else statement

$scope.$watch($messaging.isUpdated, function() {
$scope.messages = $messaging.getMessages();
if ($scope.messages.length > 0) {
    $timeout(function() {
        for (var i = 0; i < $scope.messages.length; i++) {
            if (i + 1 < $scope.messages.length) {
                $messaging.removeMessage($scope.messages[i]);
            } else { /**** unnecessay else here *****/
                $messaging.removeMessage($scope.messages[i]);
            }
        }
    }, 5000);
}});
Abderrazak BOUADMA
  • 1,526
  • 2
  • 16
  • 38