protractor: Protractor fails when app is bootstrapped with ngUpgrade
I am working on migrating an AngularJS (1.6) app to Angular (4) and now have a hybrid application, bootstrapped with NgUpgrade. This seems to have completely broken my Protractor tests.
Forgive me if this is just something I am doing wrong, but I can’t find any answer as to why this won’t work.
The error I get is:
Failed: Timed out waiting for asynchronous Angular tasks to finish after 11 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
While waiting for element with locator - Locator: By(css selector, .toggle-summary-button)
Hybrid app changes
The application seems to run fine, and both AngularJS and Angular components are working as expected. The changes I made in the bootstrapping process are:
1 Removed ng-app from html tag:
<html lang="en" *ng-app="myapp">
2 Added an AppModules (@NgModule) etc.
3 Used NgUpgrade to bootstrap the app:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { UpgradeModule } from '@angular/upgrade/static';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.body, ['myapp'], {strictDi: true});
});
Protractor tests
Based on the error above, the problem seems to be related to whatever Protractor does when it is waiting for Angular. I have a beforeEach block which loads the login page, fills in the details and logs in. Weirdly it is still opening the page and entering text into the username field, but then it fails to go any further.
I have tried, with no success:
- adding “rootElement: ‘body’” to my protractor config file
- adding “ng12Hybrid: true” to my protractor config file - I get a message saying that it should no longer be needed as it auto detects.
- increasing the allScriptsTimeout setting from 11000 to 60000 and it still times out.
- turning off the waitForAngularEnabled setting. This solves the problem with the login fields, but then none of my http mocks work and the tests fail.
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 13
- Comments: 51 (10 by maintainers)
Hi - we are working on a change to let you configure which tasks Protractor waits for, that can help you handle the above issue. This is a broad change involving Zone.js and Angular and don’t have specific ETA(will be in the order of weeks). Will update on progress here.
From: Castaway(s?) on Island of AngularJS stranded by Protractor Issue #4290 To: @vikerman @juliemr
Ahoy! Could we get an update on this issue? Last update on Oct 16 was an ETA in the “order of weeks”. Or, please close this issue so we can move on.
These are the rafts I’m currently considering:
waitForAngularEnabled(false)
for all tests as suggested by @vladotesanovic. I’m skeptical of this for an entire app but I haven’t tested it. I usewaitForAngularEnabled
in only one situation now and only for a very specific case.What I’ve discovered so far
After some digging I’ve discovered one of the causes of the timeout I’m seeing (or one of them, I suspect it’s not the only one). It turns out that one of the dependencies I’m using is calling
window.setTimeout()
which is preventing Protractor from syncing (in my case, it’s angular-moment.This ran fine when my app was running just AngularJS but causes a timeout issue when bootstrapped as a hybrid app (presumably because of the way it is run in zones).
Example Repo
I’ve created an example repo at https://github.com/mattwilson1024/angular-hybrid. It’s a hybrid (NgUpgrade) app with the following parts:
The master branch contains the working app and a single protractor tests which passes no problem.
The angular-moment branch adds a dependency on the
angular-moment
library and uses itsam-time-ago
directive to add a label to the page showing how many years ago it is since the first episode. The app itself runs fine, but the protractor test now fails to sync.Timeouts
To confirm this is happening I tried adding various different kinds of timeout to the AngularJS controller, with the following results. This confirmed that $interval works, but $timeout, setInterval or setTimeout do not.
Next Steps
Having managed to track down what’s happening I’m still not sure what the solution is. I understand that it may be necessary to run certain pieces of code “outside the Angular zone”, though I am not sure how to do this from AngularJS segments where I don’t have a TypeScript class in which to inject the zone etc.
Even then, if the problem code is happening inside a third party library (as is the case in my example with angular-moment) - what would be the approach for working around this problem to get the tests working as they did before upgrading to a hybrid setup?
Throwing another vote in here, we are in the process of upgrading our app to Angular and we are currently using a piecemeal application of
waitForAngularEnabled(false)
to get these to pass without putting the entire suite at greater risk. It’s extraordinarily unideal, though, so a better solution would be greatly appreciated.Our team is in the process of upgrading our complex application and protractor tests are failing when bootstrapped with NgUpgrade. We had to revert our changes because now we cannot deal with all of the failing tests which are false positives.
Where are we with this issue? I’m worried I’m going to have to move to a different testing framework so we can keep our plans of upgrading Angular.
We are having the same problem. Can someone from protractor update us on this please?
We are halfway into upgrading our app from Angular V1 and hate to have this cause is to go back to V1 and ditch all the work done for the upgrade to Angular V5 so far.
In our situation, tests can be written the generic (without Angular sync) way (
browser.ignoreSynchronization = true
using ECs, etc.), but fail when settingbrowser.ignoreSynchronization = false;
with"Error while waiting for Protractor to sync with the page: "Cannot read property '$$testability' of undefined"
This basically nullifies our entire E2E automated test suite.
Using Protractor 5.1.2, if I set
ng12Hybrid: true
I get the message:So obviously the angular.io documentation is out of date here: https://angular.io/guide/upgrade#e2e-tests
Famous last words from self same document:
In light of that statement on angular.io:
@heathkit Thanks for the tip about running inside and outside of zones, but to be honest we are looking for a solution that runs with our existing AngularJS code, as is. We have an extensive Protractor suite which relies on implicit AngularJS synchronization. Our goal is to migrate our AngularJS to Angular using ngUpgrade, but this issue with not being able to run our existing Protractor suite in Hybrid upgrade mode has thwarted our initiative. A sensible fix would be most welcome right about now.
If we’re doing something fundamentally wrong, we’d sure like to know what.
This thread is disheartening to see. Is there ANY update on this?
Thanks again for replying. The error detail is below. A few things to note:
My test file has a
beforeAll
block which is supposed to fill out the username and password fields to login. It fills the username field successfully but then times out inwaitForAngular
and doesn’t fill in password field. This is what the first half of the error seems to be about. The second half is the actual failing test (which makes sense, because it failed to login so the element is not present).The error suggests the problem may stem from an HTTP call, so maybe not $interval or $timeout.
I don’t have much on the Angular side at the moment, it is nearly all still AngularJS. It doesn’t get as far as showing the new Angular component because it doesn’t manage to login (so I doubt the http call is to do with the Angular 4 component)
Same issue here.
@vikerman is there any update or workaround?
First thank you to the Protractor team/contributors. Protractor has been the bedrock for my app to have very few production issues over dozens of releases over 3 years, over 200 services, directives, components, controllers with about 400 e2e Protractor tests.
This is a good reminder how much I rely on Protractor but this issue has stopped my Angular upgrade in its tracks after having dedicated 2 weeks committed to just bootstrapping ngUpgrade and the ancillary needs. That’s done but now the e2e tests can’t be run and deployment is stopped without a visible path forward (yet).
Thanks to the great info from @mattwilson1024, I’ve made sure
$timeout
,setTimeout
,setInterval
are not used in our app.But, I’ve found that
angular-ui-bootstrap
(v.2.5.6) andui-select
(v0.19.8) do use$timeout
. I use both libraries extensively. It seems a lot of others rely on them as well - angular-ui-bootstrap - 400k npm downloads/mo, ui-select 100k+/mo.angular-ui-bootstrap
created an issue in 2015 to replace $timeout with $interval specifically due to this issue but unfortunately has decided “this bug should be fixed in protractor and not here. Closing this.” Also,angular-ui-bootstrap
is no longer providing updates in liu of their Angular version @ng-bootstrap/ng-bootstrap which is in beta.I’m surprised when I see significantly used angular libraries that don’t ensure Protractor validity. On that, I agree with @heathkit that those libraries need to fix themselves. But, I’m hoping that Protractor can step in and mitigate it all with one fell swoop. Even a nice workaround would be golden. I’m disappointed that I’ll have to back out my ngUpgrade for now until I dig out all those
$timeout
dependencies and, possibly, rather than just including ngGrade and starting to pick off items for upgrade when feasible, I likely have a dependency to upgrade or migrate from those 3rd party items along with adding ngUpgrade. Ugh.I am continuing to research for my app (specific culprits causing the timeout(s), how to move forward, etc) and will report back anything that might be of use to others.
Where I’ve found $timeout: angular-ui-bootstrap v2.5.6
ui-select
angular-resource
$resource
injects and uses$timeout
angular-filter ($window.setTimeout
)We don’t really have a good answer for the case where a third party app is setting a long timeout. At the moment, all you could do is turn off
waitForAngular
and rely on ExpectedConditions, but that’s not ideal. I am working on adding a feature to Protractor that will give people more control over which macrotasks their tests wait for, but that’s still a ways off.Hi @vikerman - Any news on the changes to let you configure which tasks Protractor waits for?
@vikerman Is there any update on this?
My solution:
In this Repository:
https://github.com/Innovic-io/protractor-angular-hybrid-app
Wait for app here: https://github.com/Innovic-io/protractor-angular-hybrid-app/blob/master/src/models/angular.ts#L9
I came up with a temporary hack to solve this issue by overriding the
waitForAngular
function with the below logic.It appeared that above solution with disabling zone timers messes up with Obserables, (ex. asyncValidators). Solution that finally worked for me is monkey-patching setTimeout.
@maurycyg patchTimer -> patchMethod function should do the trick
@gkalpak You’re right that this is more of an Angular-specific issue, but people are encountering it when they use ngUpgrade with a large AngularJS app.
In AngularJS, Protractor would only wait for async tasks started with
$browser.defer()
- basically just$timeout
and$http
calls in the application code. Once someone starts using ngUpgrade, their existing Protractor tests will start timing out due to longsetTimeout
calls (potentially from third-party libraries) where they didn’t before. So mostly, the issue is that ngUpgrade is working as intended, and now people’s AngularJS app and Protractor tests are suddenly subject to Angular (2+) semantics.