The Problem Description:
We've recently got this infamous error while opening one of the pages in our application in a Protractor end-to-end test:
Failed: Timed out waiting for asynchronous Angular tasks to finish after 50 seconds. This may be because the current page is not an Angular application.
This happens on a browser.get("/some/page/");
call in one of our tests:
describe("Test", function () {
beforeEach(function () {
browser.get("/some/page/");
});
it("should test something", function () {
// ...
});
)};
And, what is weird about our case, is that the error is not thrown on any other page in our Angular web application - Protractor syncs with Angular without any problems. ng-app
location-wise things are the same across all the pages - ng-app
is defined on the root html
tag:
<html class="ng-scope" lang="en-us" ng-app="myApp" ng-strict-di="">
The behavior is consistent - every time we navigate to this page with browser.get()
, we get this error. Any time we navigate to any other page in our app, sync works.
Note that, of course, we can turn the sync off for this page and treat it as non-angular, but this can only be considered as a workaround.
The Questions:
What else can cause Protractor-to-Angular sync fail? What should we check?
And, in general, what is the remended way to debug sync problems in Protractor?
Using currently latest Protractor 5.5.1, Angular 1.5.6.
The Problem Description:
We've recently got this infamous error while opening one of the pages in our application in a Protractor end-to-end test:
Failed: Timed out waiting for asynchronous Angular tasks to finish after 50 seconds. This may be because the current page is not an Angular application.
This happens on a browser.get("/some/page/");
call in one of our tests:
describe("Test", function () {
beforeEach(function () {
browser.get("/some/page/");
});
it("should test something", function () {
// ...
});
)};
And, what is weird about our case, is that the error is not thrown on any other page in our Angular web application - Protractor syncs with Angular without any problems. ng-app
location-wise things are the same across all the pages - ng-app
is defined on the root html
tag:
<html class="ng-scope" lang="en-us" ng-app="myApp" ng-strict-di="">
The behavior is consistent - every time we navigate to this page with browser.get()
, we get this error. Any time we navigate to any other page in our app, sync works.
Note that, of course, we can turn the sync off for this page and treat it as non-angular, but this can only be considered as a workaround.
The Questions:
What else can cause Protractor-to-Angular sync fail? What should we check?
And, in general, what is the remended way to debug sync problems in Protractor?
Using currently latest Protractor 5.5.1, Angular 1.5.6.
https://github./angular/protractor/blob/master/docs/timeouts.md#how-to-disable-waiting-for-angular
. The solution which they are telling is same to turn the sync off or probably use driver.browser.get()
instead of browser.get()
.
– Kishan Patel
Commented
Mar 18, 2017 at 10:55
$http
and $timeout
- protractor will wait for both of those services to finish. If it's caused by $timeout
then you should simply replace it with $interval
and as for $http
there is no workaround unless you want to write an XHR service and use it instead of $http
– maurycy
Commented
Mar 20, 2017 at 22:39
$timeout
issue before (don't recall the $http
related one). But, how would I know for sure what exactly is causing the problem? How, through debugging, I can detect the reason for a sync failure?..Thank you.
– alecxe
Commented
Mar 20, 2017 at 22:44
Ok, so that question intrigued me so I came up with a programmatic solution on how to determine what protractor is waiting for:
var _injector = angular.element(document).injector();
var _$browser = _injector.get('$browser');
var _$http = _injector.get('$http');
var pendingTimeout = true;
//this is actually method that protractor is using while waiting to sync
//if callback is called immediately that means there are no $timeout or $http calls
_$browser.notifyWhenNoOutstandingRequests(function callback () {
pendingTimeout = false
});
setTimeout(function () {
//this is to differentiate between $http and timeouts from the "notifyWhenNoOutstandingRequests" method
if (_$http.pendingRequests.length) {
console.log('Outstanding $http requests', _$http.pendingRequests.length)
} else if (pendingTimeout) {
console.log('Outstanding timeout')
} else {
console.log('All fine in Angular, it has to be something else')
}
}, 100)
Here in the plunker http://plnkr.co/edit/O0CkpnsnUuwEAV8I2Jil?p=preview you can experiment with the timeout and the $http call, my delayed endpoint will wait for 10 seconds before resolving the call, hope that this will be helpful for you
I agree with @maurycy that the issue is $http/$timeout related. The simple fix is normally to replace $timeout with $interval as documented here: https://github./angular/protractor/blob/master/docs/timeouts.md
Remendations:
merge these sane defaults:
allScriptsTimeout: 60000, // 1 minute
jasmineNodeOpts: {
defaultTimeoutInterval: 300000
// 5 minutes. Allows for 5 mands spanning the full synchronization timeout.
}
If you want to find the culprit of $http/$timeout, I would use angular decorators to apply custom logic around these services. This is also a good way to mock angular services accessing third party services. https://docs.angularjs/guide/decorators
//DISCLOSURE: Unlinted & Untested.
beforeAll(() => {
browser.addMockModule('culpritMock', () => {
angular.module('culpritMock', [])
.config(['$httpProvider',
$httpProvider => $httpProvider.interceptors.push('httpCounterInterceptor')
])
.factory('httpCounterInterceptor', ['$q', '$window', ($q, $window) => {
if ($window.httpCounterInterceptor == null) {
$window.httpCounterInterceptor = {};
}
return {
request: config => {
$window.httpCounterInterceptor[config.url] = 'started';
return config;
},
response: response => {
$window.httpCounterInterceptor[response.config.url] = 'pleted';
return response;
},
responseError: rejection => {
$window.httpCounterInterceptor[rejection.config.url] = 'error';
return $q.reject(rejection);
}
};
}])
.decorator('$timeout', ['$delegate', $delegate => {
const originalTimeout = $delegate;
function modifiedTimeout() {
console.log(arguments);
return originalTimeout.apply(null, arguments);
}
modifiedTimeout.cancel = function(promise) {
return $delegate.cancel(promise);
}
return modifiedTimeout;
}]);
});
});