Selenium-webdriver JS - how to wait until an element is visible

16,116

It most likely is a Promise issue. Try this instead:

exports.waitForElement = function (locator, timeout) {
  var timeout = timeout || DEFAULT_TIMEOUT;
  return this.wait(until.elementLocated(locator), timeout);
};

exports.waitForVisibleElement = function (locator, timeout) {
  var timeout = timeout || DEFAULT_TIMEOUT;
  var element = this.wait(until.elementLocated(locator), timeout);
  return this.wait(new until.WebElementCondition('for element to be visible ' + locator, function() {
    return element.isDisplayed().then(v => v ? element : null);
  }), timeout);
};

Usage:

driver.get("...");

driver.waitForElement(By.id("..."), 2000).getText().then(function(text){
  console.log(text);
});

driver.waitForVisibleElement(By.id("..."), 2000).getText().then(function(text){
  console.log(text);
});
Share:
16,116
NeuronQ
Author by

NeuronQ

Programmer with 7+ yrs experience (backend, full-stack, a bit of ML-infra.). šŸ’” ā‡’ šŸ­ - Can take complex projects from idea to delivery. āš™ļø ā‡’ šŸ¤– - Looking to bring my solid software-engineering experience to the areas of machine-learning- engineering and data-science-engineering - this is what Iā€™m passionate about and where I feel I can make the strongest positive impact!

Updated on June 08, 2022

Comments

  • NeuronQ
    NeuronQ almost 2 years

    Using selenium-webdriver (api docs here), how can you wait for an element to be visible?

    I have the following functions, part of a home-made set of testing helpers, and the first one works but the second one fails (eg. it times out wating for and element to be visible even if it exists - as confirmed by the first function that works - and is visible - as confirmed by all imaginable tests and inspections of the page html/css/js).

    Here they are:

    /**
     * Wait for an element to exist
     * 
     * @param  {object} locator
     * @param  {int} timeout (ms)
     * 
     * @return {Promise<element>}
     */
    // !! THIS WORKS OK
    exports.waitForElement = function (locator, timeout) {
      var waitTimeout = timeout || DEFAULT_TIMEOUT;
    
      return this.wait(until.elementLocated(locator), waitTimeout)
        .then(() => {
    
            return this.findElement(locator);
          });
    };
    
    
    /**
     * Wait for an element to exist and then wait for it to be visible
     *
     * IMPORTANT: this is probable what you want to use instead of
     *   waitForVisibleElement most of the time.
     * 
     * @param  {hash} locator
     * @param  {number} timeout
     * 
     * @return {Promise<element>}
     */
    // !! THIS FAILS TO WORK AS EXPECTED
    exports.waitForVisibleElement = function (locator, timeout) {
      var waitTimeout = timeout || DEFAULT_TIMEOUT;
    
      return this.waitForElement(locator, waitTimeout) 
        .then(el => {
          console.log('--- element found:', el);
          return this.wait(until.elementIsVisible(el), waitTimeout)
    
            .then(() => {
              console.log('--- element visible!');
              // this is to make sure we are returning the same kind of
              // promise as waitForElement
              return this.findElement(locator);
            });
    
        });
    };
    

    ...I tested in multiple contexts, so it's no other cause of the problem then the code inside waitForVisibleElement but I can't seem to find any reason for why it does not work!


    As clarification, this for that code ends up being the webdriver instance (the result of new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build()) after an augment method monkeypatches a given webdriver object... probably a questionable design pattern, but no the cause for my problem here :)


    UPDATE: Apparently this only happens for XPath locators, like { xpath: '//*[contains(text(), "first name")]' }... not that it makes any more sense now. Also, it's the same for Firefox, so it's not a weird chrome-webdriver thingy...