protractor: Protractor hangs when returning ElementFinders or WebElements from `map()`

Test setup: Non - Angular page, with browser.ignoreSynchronization = true Browser: IE9 Protractor: 2.1.0 webdrivers up to date

Description: In the below code I am using element.all within Page Objects to find elements. When run the test will hang indefinitely. This happens when I try to assign a ElementFinder from the array to a member variable (so I can have reference for future use).

If I comment the following two lines then the tests will run to completion.

self.rowWebElement = el;  // commenting this line will not hang
  And
'RowWebElement': el.getWebElement(), // commenting this line will not hang

So why is it hanging?

Code:

var TablePage = function() {
  this.uri = "http://www.w3schools.com/html/html_tables.asp";

  this.open = function() {
    browser.get(this.uri);
  };

  this.getPageResults = function() {
    var self = this;
    var rows = element.all(by.className('reference'));
    this.results = rows.map(function(el, idx) {
      console.log('Processing row');

      self.rowWebElement = el;  // commenting this line will not hang
      var cols = el.all(by.tagName('td'));

      return {
       'Number': cols.get(0).getText(),
       'First Name': cols.get(1).getText(),
       'Last Name': cols.get(2).getText(),
       'Points': cols.get(3).getText(),
       'RowWebElement': el.getWebElement(), // commenting this line will not hang
       'Index': idx
      }
    });
    return this.results;
  };

  this.getRow = function(fName) {
    var self = this;
    var deferred = protractor.promise.defer();

    var filtered = [];

    self.results.then(function (grid) {
      for (var i=0; i < grid.length; i++) {
        var result = grid[i]['First Name'];
        if (result == fName ) {
          console.log('Row matching criteria :' + result);
          filtered.push(grid[i]);
        }
      }
      deferred.fulfill(filtered[0]);
    });

    return new TableRowObjectPromise(deferred.promise);
  };

};

var TableRowObjectPromise = function (trPromise) {
  // Wrap the promise
  var self = this;
  this.getValue = function(colName) {
    return trPromise.then(function(tr) {
      return tr[colName];
    });
  };
};

/** ----------------------------------------------------------------
 *  begining of test 
 */ 
var tablePage = new TablePage();
// Tests begin
describe('Table page', function() {

  beforeAll(function() {
    browser.ignoreSynchronization = true
  });

  it('- goto table page', function() {
    tablePage.open();
  });

  it('- get table row objects', function() {
    var pr1 = tablePage.getPageResults();
    var tr = tablePage.getRow('Eve');
  });
});

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 2
  • Comments: 34 (10 by maintainers)

Most upvoted comments

We are also seeing this with v5.3.2, this is in an environment where we use the new async/await and control flow disabled.

By the way, here is the hotfix we are using in our tests: in protractor.conf file inside onPrepare:

require("protractor").ElementArrayFinder.prototype.map = function(mapFn) {
    return this.reduce((arr, el) => arr.concat(mapFn(el, arr.length)), []);
};

Still seeing this with Protractor 5.1.2. @juliemr please reopen.

Same problem here. After playing a little - found short reproducible code

$$('.audio-player-item').map(function (elemt, index) {
            return {param: elemt};
     });

But with this - works fine:

$$('.audio-player-item').map(function (elemt, index) {
            return {param: elemt.getText()};
     });

Still seeing the hang on Protractor 4.0.14.

const divs = element.all(by.css("div.my-div"));

divs.reduce((array, div) => array.concat({myDiv: div}), []); // WORKS

divs.map(div => { return {myDiv: div}; }); // HANGS

This is not good. Operations like map should not fail like this for basic use-cases.

Is there a reason why map() is using webdriver.promise.fullyResolve(), instead of webdriver.promise.when()?

https://github.com/angular/protractor/blob/master/lib/element.ts#L519

I experimented with changing it to when and didn’t get any failures in the Protractor test suite.

Needing to resolve every nested promise-like property of every mapped object does not seem like a common requirement. But mapping to an object with an ElementFinder field does seem like a common requirement. And I’m not sure why you would want the full resolution to happen automatically? It seems like very non-obvious API design.

I would contend that it is much better to remove the fullyResolve() call and fix this bug. If users need this behaviour, they can always call webdriver.promise.fullyResolve() themselves on whatever they intend to return from the map function.