0

I am very new to TypeScript and protractor and would like to put all the extracted values from a drop list inside an array so that I can validate it from another page.

export class AdditionalCostPage extends BasePage {
  getAllUsageCategoryElements() {
    var usageCategory: string[] = [];

    element
      .all(
        by.xpath(
          "//p-dropdown[@name='usageCategory']/div/div[3]/div/ul/li[*]/span"
        )
      )
      .each(function(element, index) {
        element.getText().then(function(text) {
          console.log('list text from drop list  is ' + text);
          usageCategory.push(text);
        });
      });

    console.log('Size of the array is ' + usageCategory.length);
  }
}

In the result the size of the usageCategory is 0 and also I noticed that the size 0 is printed before "console.log("list text from drop list is " + text);" gets executed. Please suggest anyone. Thanks in Advance.

Robbie Milejczak
  • 5,664
  • 3
  • 32
  • 65
Deb
  • 35
  • 1
  • 10
  • 1
    the problem is that `element.getText()` is returning a Promise, so it's asynchronous. Check this out: https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call – Robbie Milejczak Jan 23 '19 at 19:45

2 Answers2

0

This is because the .each method from the ElementArrayFinder returns a promise. See http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.each

Switching to async / await

You should think about switching to async / await tests. It will make promises easier to work with. You will need to specify SELENIUM_PROMISE_MANAGER: false in your Protractor config. There are other examples of async / await tests that have been answered in StackOverflow. Just remember you'll need to await each async method.

// When adding in async to this method, by default it returns a Promise<void>.
async getAllUsageCategoryElements() {
  // Note: You should not use xpath as a selector since it makes
  // your tests brittle.
  // See Locator Strategies in http://www.protractortest.org/#/style-guide
  // Also nit: think about using "const" or "let" over "var".
  const usageCategory = element.all(
      by.xpath(
        "//p-dropdown[@name='usageCategory']/div/div[3]/div/ul/li[*]/span"
      );

  // Think about using fat arrows instead of "function"
  await usageCategory.each(async (element, index) => {
    const text = await element.getText();
    console.log('list text from drop list  is ' + text);
  });

  // You could push the ElementFinder object to an array and find the length
  // or you could get the count. See
  // http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.count
  console.log('Size of the array is ' + await usageCategory.count());
}
cnishina
  • 5,016
  • 1
  • 23
  • 40
0

The problem is implementation above did not handle the async properly.

Size of the array is 0
list text from drop list  is a
list text from drop list  is b
list text from drop list  is c
list text from drop list  is d

Consider using await async, it would make a bunch of these issues much cleaner.

async getAllUsageCategoryElements() {
    let usageCategory: string[] = [];

    const elms = await element
      .all(
        by.xpath(
          '//p-dropdown[@name='usageCategory']/div/div[3]/div/ul/li[*]/span'
        )
      );

    for (var i = 0; i < elms.length; i++) {
      usageCategory.push(await elms[i].getText());
    }

    return usageCategory;
}

From where you will be calling this function

const abc = await getAllUsageCategoryElements();
console.log('Size of the array is ' + abc.length);
Rochan
  • 14
  • 3