Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protractor's waitForAngular() fails on angular-webapp (appium/chrome on real device)

I'm (newly) using protractor to run e2e cucumber tests. I got a web-app which is angularJS based. I'm using appium to remotely run the test on a real android device. Here are the versions i'm using :

windows8.1
[email protected] (with submodule [email protected])
[email protected]
android device with 4.4.4

my protractor configuration (extracts), corresponding to https://github.com/angular/protractor/blob/master/docs/browser-setup.md:

currentDeviceUDID = (...);
var appToTestURL = 'http://my.website.com:9000/app/index.html';

exports.config = {
  seleniumAddress: 'http://localhost:4723/wd/hub';
  chromeOnly: false,
  specs: ['features/sample.feature'],

  capabilities: {
    browserName: 'chrome',
    'appium-version': '1.0',
    platformName: 'Android',
    platformVersion: '4.4.4',
    udid: currentDeviceUDID
  },

  baseUrl: appToTestURL

  framework: 'cucumber',
  cucumberOpts: {
    require: 'features/stepDefinitionsSample.js',
    tags: '@dev',
    format: 'progress'
  },

  // configuring wd in onPrepare
 onPrepare: function () {
    var wd = require('wd'),
    protractor = require('protractor'),
    wdBridge = require('wd-bridge')(protractor, wd);
    wdBridge.initFromProtractor(exports.config);
  },

  allScriptsTimeout: 30000,
  getPageTimeout: 30000
};

As you can see, i have replaced the protractor's webdriver url with the appium webdriver. i start the appium from commandline with "appium &", then i run the test with "protactor cucumbertest.conf"

The phone opens chrome browser and navigates to the url i give it with "browser.get(url)"

the problem is the following: the call waitForAngular(), which is asynchronously waiting for the website to load and on all open http request (as far as i understand), is not executed sucessfully on the phone. the phone does not react to the call, and the webdriver proxy returns a 500.

Corresponding to https://github.com/angular/protractor/issues/1358, i understood that the waitForAngular() function is mixed in protractor into the calls

['getCurrentUrl', 'getPageSource', 'getTitle'];

Behind waitForAngular() in the file protractor.js is the function below, which is proxied to the phone:

functions.waitForAngular = function(selector, callback) {
  var el = document.querySelector(selector);
  try {
    if (angular.getTestability) {
      angular.getTestability(el).whenStable(callback);
    } else {
      angular.element(el).injector().get('$browser').
      notifyWhenNoOutstandingRequests(callback);
    }
  } catch (e) {
    callback(e);
  }
};

Additional information: when i stimulate an error on the webdriver (browser) object, the error message points to the chromedriver.exe inside the protractor directory. i dont understand why the error is not from appium's chromedriver


so tldr; without the successful call waitForAngular, i cannot (stable or at all) access elements on the page on the phone, so not testing. maybe im misunderstanding some fundamental configuration detail here, all hints are welcome.

edit: added appium server logs here: http://pastebin.com/vqBGUdXH

like image 466
mojjj Avatar asked Oct 10 '14 12:10

mojjj


1 Answers

I assume i have identified the problem. Appium and Protractor work fine.

My angularJS app causes the issue. It uses $timeout for polling (im forced on angular 1.07 which has no $interval). This causes protractor to expect the page to be still in the loading stage and not finished. Therefore the function call waitForAngular() never returns and the test timeouts after the specified timeout-timespan.

This behaviour is expected and known, also documented (better read doc first ;) ) at http://angular.github.io/protractor/#/timeouts

The doc suggests the following for continuous polling: replace $timeout with $interval:

If your application continuously polls $timeout or $http, it will never be registered as completely loaded. You should use the $interval service (interval.js) for anything that polls continuously (introduced in Angular 1.2rc3).

For now, i fixed the issue another way: disable the built-in angular sync and manually sync

this.Before(function(next){
    ptor = protractor.getInstance();
    ptor.ignoreSynchronization = true; //disables waitForangular()
    next();
});
  1. Sync method 1:

    //at a testcase, wait for an element to be visible with a promise/then
    browser.wait(function () {
        element.all(by.css('.myCssClass')).then(function (items) {
            items[0].getText().then(function (text) {
                console.log(text);
            });
        });
        return true;
    }
    
  2. Sync method 2 :

    // "eventually" (chai-as-promised) internally uses "promise" (and therefore acts like "then")
    browser.get(url);
    expect(browser.getTitle()).to.eventually.equal("connect me").and.notify(next);
    
like image 111
mojjj Avatar answered Nov 12 '22 13:11

mojjj