1

I'm writing test using protractor and it always waits when $timeout will finished.

If I use ignoreSynchronization = true; it stops to wait and test runs ahead. But for my test I should set back ignoreSynchronization = false;. And when I set it to false, protractor stops and begin to wait for previous $timeout will finished.

I use $timeout for notification window and use this window to show validation errors. Currently I'm testing validation and it makes test extremely slow.

So the solution with ingnoreSynchronization = true is not compatible for me.

I have tried to $timeout.flush() but it cause an error $timeout.flush is not a function

I tried to use ngMock but am not sure is it possible to use it with protractor.

Does somebody resolve this issue?

Gleb
  • 1,312
  • 3
  • 18
  • 36
  • why is `ignoreSynchronization = true` not an option for you? – Gunderson Aug 30 '16 at 15:52
  • The architecture you are describing doesn't make sense. What does "I use `$timeout` for notification window...." actually mean? Why would you force angular to *wait* when you want to pop up a notification? You have tightly coupled your app logic to a UI thread by doing this this way, which is a code smell, and the slowness in your tests are also going to manifest as slowness for your users. Seems like the unit tests have done their job in identifying a place to consider a major refactor. – Claies Aug 30 '16 at 15:52

3 Answers3

1

Protractor is supposed to wait for any $timeout or $http calls to finish before executing, as part of synchronization with the Angular app. Since you use $timeout continuously, you have a few options:

  1. Change your $timeout to use $interval instead. Source: Protractor Timeouts
  2. Adjust your config file to account for $timeout, taken from the Protractor Master Conf:

Protractor will track outstanding $timeouts by default, and report them in the error message if Protractor fails to synchronize with Angular in time. In order to do this Protractor needs to decorate $timeout. CAUTION: If your app decorates $timeout, you must turn on this flag. This is false by default. untrackOutstandingTimeouts: false,

  1. Set browser.ignoreSynchronization = true to treat your app as "Non-Angular" and use Expected Conditions to wait for certain elements to be present before executing the test. Source: Expected Conditions
Gunderson
  • 3,263
  • 2
  • 16
  • 34
1

I had the same exact issue. We are using timeouts on our notification UI and it was pausing the app every time we displayed a notifications till it dismissed itself. This worked for us:

Protractor allows you to inject modules from your tests. So what we did was move our timeout duration argument into a constant, that could be set to 0 when running protractor tests.

In your module:

.constant('NOTIFICATION_CONSTANTS', {
  TIMEOUT_DURATION: 5000
})

.controller('NotificationController', function($scope, NOTIFICATION_CONSTANTS) 
{
  $scope.showNotification = function() {
    $timeout(function() { hideNotification(); }, NOTIFICATION_CONSTANTS.TIMEOUT_DURATION)
  };
})

in your protractor test:

...
beforeAll(function() {
  browser.addMockModule('testConfig', function() {
    angular.module('testConfig', []).run(function(NOTIFICATION_CONSTANTS) {
      NOTIFICATION_CONSTANTS.TIMEOUT_DURATION = 0;
    });
  });
});
...

It wont dismiss all $timeouts by default, but if they're created by you and you know which ones you want to dismiss you can target them like this.

0

It seems that you are not waiting the resolution of the promise to set ignoreSynchronization to true or false. I would try to push a function on the control flow to set the property:

// disable the synchronization
browser.controlFlow().execute(function() {
  browser.ignoreSynchronization = true;
});

// execute some asynchronous code
...

// enable the synchronization
browser.controlFlow().execute(function() {
  browser.ignoreSynchronization = false;
});
Florent B.
  • 41,537
  • 7
  • 86
  • 101