6

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
protractor@1.3.1 (with submodule selenium-webdriver@2.43.5)
appium@1.3.0beta1
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

Emna Ayadi
  • 2,430
  • 8
  • 37
  • 77
mojjj
  • 625
  • 8
  • 18
  • 1
    Your capabilities are all correct and the appium server is working correctly. The 500 returned is after all of the commands are processed. Try posting on discuss.appium.io you'll get the best response there – Jess Oct 10 '14 at 16:44

1 Answers1

4

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);
    
cramopy
  • 3,459
  • 6
  • 28
  • 42
mojjj
  • 625
  • 8
  • 18
  • 1
    i must add here that you can use $timeout as long as you correctly run the .cancel function on $timeout. replacing $timeout with $interval is not that obvious because they do not have the exact behavior. Check my post http://stackoverflow.com/questions/40832962/angular-1-5-timeout-using-a-httpinterceptor and the comment on how to cancel the $timeout, after that you can still continue using the waitForAngular – Ben Croughs Dec 19 '16 at 10:56