I have an RESTful API and now I am developing a angular2 app which uses data services to call the API. I then wanted to implement end to end tests using protractor. I wanted to start very low level, so I made tests to just check if my components are present when clicking on a link. The test looks like this:
describe('my-webclient', () => {
it('app should load', () => {
browser.get('/');
expect(element(by.css('my-app')).isPresent()).toBe(true);
});
it('app should have a top navigation', () => {
expect(element(by.css('my-nav-top')).isPresent()).toBe(true);
});
it('app should have a side navigation', () => {
expect(element(by.css('my-nav-side')).isPresent()).toBe(true);
});
it('app should have a content', () => {
expect(element(by.css('my-content')).isPresent()).toBe(true);
});
it('app should load the overview for route "/"', () => {
expect(element(by.css('my-overview')).isPresent()).toBe(true);
});
});
The problem is, that the overview component makes HTTP requests in a ngOnInit
function using some data service. Protractor will then just block forever, although the HTTP requests have been completed long ago.
When I monitor the test, I can see how the app loads, including all the data which was fetched from my API. Then nothing happens and protractor will eventually crash, saying that the timeout was reached.
The exact error message is:
Failed: Timed out waiting for Protractor to synchronize with the page after 11 seconds. Please see https://github.com/angular/protractor/blob/master/docs/faq.md
While waiting for element with locator - Locator: By(css selector, my-app)
While searching for help on google, I came across several hints that protractor will wait for angular to finish everything. Well, this is exactly what I want. When I monitor the network tab in chrome's developer tools (F12), then nothing appears shortly after the app has loaded. So there are no more pending requests or anything for protractor to wait for. Still, it does, and I just don't know why.
So, here's my question: Do I have to consider anything special when dealing with data services making HTTP requests? Or: What can I do to debug why protractor is still waiting until the timeout hits.
By the way, it is definetly the data service. If I comment out everything inside OverviewComponent::ngOnInit
, then the tests will pass just fine and the way I expect it.
(Short answer - any long-running tasks (timeouts, intervals, Promises or Observables) will block testing if not disposed correctly. Http cleans up after itself, so it is likely not your problem.)
Http is most likely not your issue.
Angular 2 uses zones to detect it's stable/unstable state, and Protractor uses a specific injectable class - the Testability Class - to determine app state. Testability of all existing Angular2 apps in the page is exposed to the window object, which is how Protractor keeps tabs on Angular 2.
Angular uses zones to maintain change detection. In userland, we get some control over where our code happens (in or out of angular) through NgZone, useful for two things - testing and performance. Any task run in an angular zone (which is what happens unless we specify otherwise) will trigger change detection, and any async task we wait for flags Testability as unstable.
This means a number of things may be interfering with your app's stability:
To fix this, you need to run your tasks outside angular, using NgZone:
this.ngZone.runOutsideAngular(() => {
this._sub = Observable.timer(2000)
.subscribe(() => this.ngZone.run(() => {this.content = "Loaded!"}));
})
See a working example in this plunk, and this talk by Julie Ralph for more details - she's the brains behind Protractor.
So Tiagos answer definitely pointed me in the right direction. I was scared that it might be an issue with my observables. Was relieved to discover it was just a window.setInterval
() I was using to refresh my jwt. This was solved as follows:
constructor(private ngZone: NgZone) {
}
this.ngZone.runOutsideAngular(() => {
Observable.interval(4 * 60 * 1000)
.subscribe(data => {
this.refreshJwt();
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With