doberkofler
doberkofler

Reputation: 10431

Wait for multiple elements to be visible in selenium webdriver JavaScript bindings

The selenium webdriver bindings for JavaScript allow to wait for an element to be visible by combing two wait commands like in the following example:

const timeout = 1000;
const locator = webdriver.By.id('test');
driver.wait(webdriver.until.elementLocated(locator, timeout).then(function() {
   that.findElement(locator).then(function(element) {
      driver.wait(webdriver.until.elementIsVisible(element), timeout).then(function() {
         // element is visible!
      });
   });
});

Is there an easier way to do this and how would this be done, when we need to wait for an array of elements to be visible all together?

Upvotes: 5

Views: 5477

Answers (2)

Grim
Grim

Reputation: 2107

One of the great advantages of Promises is that you you can keep your async code linear rather than nested (callback hell from continuation passing style).

Promises give you return statements and error throwing, which you lose with continuation passing style.

In your case, you need to return the promise from your promise returning functions so you can chain your promises.

Example: Promise.all takes an array of promises and resolves once all promises resolve, if any are rejected, the array is rejected.

this.waitForElementsToBecomeVisible = function() {
    return Promise.all([
        driver.wait(webdriver.until.elementIsVisible(usernameTextField), 500),
        driver.wait(webdriver.until.elementIsVisible(firstNameTextField), 500),
        driver.wait(webdriver.until.elementIsVisible(lastNameTextField), 500),
        driver.wait(webdriver.until.elementIsVisible(createEmployeeButton), 500)
    ]);
}

Then you can chain your promise.

driver.get('https://website.com/login').then(function () {
    loginPage = new LoginPage(driver);
    return loginPage.login('company.admin', 'password')
}).then(function () {
    employeePage = new EmployeePage(driver);
    return employeePage.clickAddEmployee()
}).then(function () {
    addEmployeeForm = new AddEmployeeForm(driver);
   /**
    *
    * Wait for elements to become visible
    */
    return addEmployeeForm.waitForElementsToBecomeVisible();
}).then(function() {
    return addEmployeeForm.completeForm(employee);
}).then(function() {
    return addEmployeeForm.clickCreateEmployee();
}).then(function() {
    return employeePage.searchEmployee(employee);
});

You can see how the above example isn't nested and is alot easier to maintain. You return a promise and keep chaining instead of nesting. I hope this helps you and doesn't confuse you at all.

Upvotes: 7

Makjb lh
Makjb lh

Reputation: 457

Simple - Write a common/global function. Take single or multiple Webdriver elements as input as well as condition if you need to vary it. Loop around till end of elements and do the same Wait until each element visible.

This is how I routinely do it in my functions. in Ruby I do this

for each element in [array of elements], Condition
   [Below is same as your code if working well]
   Driver - Element wait until (Condition satisfied)
   element perform function
end

If you need to handle even multiple conditions, you can use a switch case statement. if you need to perform function only once keep the function outside

element perform function

Upvotes: 0

Related Questions