Protractor's waitForAngular() fails on angular

2020-07-11 05:53发布

问题:

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

回答1:

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);