angular: 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

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 15
  • Comments: 15 (3 by maintainers)

Commits related to this issue

Most upvoted comments

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.

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:

https://github.com/choeller/quickstart/commit/e7d4c9d0b7029f45be5d5e0cb4c12aaa1af09930

Just clone the repo and run

npm run e2e

I may have found a workaround, but I’m not very familiar with Protractor in ng2 so I don’t know if it’s a useful workaround. If you look here, you can disable protractor waiting for angular. So, in my app.e2e-spec.ts file before each request I set

browser.ignoreSynchronization = true;

You can see my full file here:

import { browser, element, by } from 'protractor';

describe('MyApp', () => {
  beforeEach(() => {
    browser.ignoreSynchronization = true;
    browser.get('');
  });

  it('should have a title', () => {
    expect(browser.getTitle()).toEqual('Ionic App');
  });
});

Is there another work around for this ? Setting browser.ignoreSynchronization = true; seems like it’ll be harder to write tests.

+1