Solvedangular bug(testing) Protractor stops working when service uses socket.io

I'm submitting a ... (check one with "x")

[x] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

It's not possible to implement e2e tests with protractor when using socket.io in the application. We are having a service, that is connecting to a websocket like this:

export class TaskService {

  socket: SocketIOClient.Socket;

  constructor(private http: Http) {
    this.socket = io(WEB_SOCKET_URL);
  }
}

just calling the io function makes protactor stop working. so my tests fails with:

[10:58:04] I/direct - Using FirefoxDriver directly...
[10:58:04] I/launcher - Running 1 instances of WebDriver
Spec started

  my-project App
     should display message saying app works
      - Failed: Timed out waiting for Protractor to synchronize with the page after 110002ms. Please see https://github.com/angular/protractor/blob/master/docs/faq.md
While waiting for element with locator - Locator: By(css selector, h1)

**************************************************
*                    Failures                    *
**************************************************

1) my-project App should display message saying app works
  - Failed: Timed out waiting for Protractor to synchronize with the page after 110002ms. Please see https://github.com/angular/protractor/blob/master/docs/faq.md
While waiting for element with locator - Locator: By(css selector, h1)

as soon as I disconnect from the socket by doing:

export class TaskService {

  socket: SocketIOClient.Socket;

  constructor(private http: Http) {
    this.socket = io(WEB_SOCKET_URL);
    this.socket.disconnect();
  }
}

all tests are running fine.
There are also already other people experiencing the same problem:
http://stackoverflow.com/questions/39162553/running-e2e-test-using-protractor-with-angular2-and-socket-io

Expected behavior

It should be possible to run e2e tests for applications using socket.io.

Reproduction of the problem

See this repo to see the error in action: https://github.com/choeller/quickstart

What is the motivation / use case for changing the behavior?

Being able to test our application ;)

Please tell us about your environment:

  • Angular version: 2.0
  • Browser: [all]
  • Language: [TypeScript]

//cc @juliemr

16 Answers

✔️Accepted Answer

What I did to fix the problem was using ngZone everywhere I have an observable that relies on socket.io.

So let's say you have this method in your service that gives you an observable on a socket.io.

...
private socket: SocketIOClient.Socket;

 public getSocketIOEvents(): Observable<SocketIOEvent> {

        if (this.socket == null) {
            this.socket = io.connect(this._socketPath);
        }

        return Observable.create((observer: any) => {
            this.socket.on('eventA', (item: any) => observer.next(new SocketIOEvent(item)));
            this.socket.on('eventB', (item: any) => observer.next(new SocketIOEvent(item)));
            return () => this.socket.close();
        });
    }

Then you need to use the ngZone service to tell Angular to create the socket outside the Angular 2 zone and then execute the callback of the Observable inside the Angular 2 zone.

import {  NgZone } from '@angular/core';
constructor(
    private socketService: SocketIOService,    ,
    private ngZone: NgZone) { }
...

ngOnInit() {

    // Subscribe to the Observable outside Angular zone...    
    this.ngZone.runOutsideAngular(() => {
      this.socketService
        .getSocketIOEvents()
        .subscribe(event => {

         // Come back into Angular zone when there is a callback from the Observable
          this.ngZone.run(() => {
            this.handleEvent(event);
          });
        });
    });

  }

This way protractor doesn't hang waiting on the socket.

Other Answers:

I reproduced the problem by doing really minimal changes to the offical starter repo. So to see the problem in action you can clone this repo:

https://github.com/choeller/quickstart

Everything I changed is in this commit:

choeller/quickstart@e7d4c9d

Just clone the repo and run

npm run e2e

More Issues: