Selenium WebDriver wait till element is displayed

Javascriptnode.jsSelenium Webdriver

Javascript Problem Overview


I have searched on Google and the SO site and I get answers for JAVA but do not seem to get answers for node.js

I have a web app that takes time to load. I would like the selenium program to wait till the page is loaded and then perform some actions.

My current code is as follows

//dependencies
var webdriver = require('selenium-webdriver'),
	util = require('util'),
	_ = require('underscore');

var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build();
var branchName =  _.isUndefined(process.argv[3]) ? 'branch' : process.argv[3], 
    hostName = _.isUndefined(process.argv[2]) ? 'localhost' : process.argv[2],
    appTmpl = 'http://%s/%s',
    username = 'xxxx',
    password = 'xxxx';
var appUrl = util.format(appTmpl, hostName, branchName);

driver.get(appUrl);
driver.findElement(webdriver.By.name("username")).sendKeys(username);
driver.findElement(webdriver.By.name("password")).sendKeys(password);
driver.findElement(webdriver.By.name("login_button")).click();
driver.quit();

The error I get is:

    C:\Work\study\selenium\node_modules\selenium-webdriver\lib\webdriver\promise.js:1643
      throw error;
            ^
NoSuchElementError: no such element
  (Session info: chrome=37.0.2062.103)
  (Driver info: chromedriver=2.10.267521,platform=Windows NT 6.1 SP1 x86_64)
    at new bot.Error (C:\Work\study\selenium\node_modules\selenium-webdriver\lib\atoms\error.js:109:18)
    at Object.bot.response.checkResponse (C:\Work\study\selenium\node_modules\selenium-webdriver\lib\atoms\response.js:106:9)
    at C:\Work\study\selenium\node_modules\selenium-webdriver\lib\webdriver\webdriver.js:277:20
    at C:\Work\study\selenium\node_modules\selenium-webdriver\lib\goog\base.js:1243:15
    at webdriver.promise.ControlFlow.runInNewFrame_ (C:\Work\study\selenium\node_modules\selenium-webdriver\lib\webdriver\promise.js:1539:20)
    at notify (C:\Work\study\selenium\node_modules\selenium-webdriver\lib\webdriver\promise.js:362:12)
    at notifyAll (C:\Work\study\selenium\node_modules\selenium-webdriver\lib\webdriver\promise.js:331:7)
    at resolve (C:\Work\study\selenium\node_modules\selenium-webdriver\lib\webdriver\promise.js:309:7)
    at fulfill (C:\Work\study\selenium\node_modules\selenium-webdriver\lib\webdriver\promise.js:429:5)
    at C:\Work\study\selenium\node_modules\selenium-webdriver\lib\webdriver\promise.js:1406:10
==== async task ====
WebDriver.findElement(By.name("username"))
    at webdriver.WebDriver.schedule (C:\Work\study\selenium\node_modules\selenium-webdriver\lib\webdriver\webdriver.js:268:15)
    at webdriver.WebDriver.findElement (C:\Work\study\selenium\node_modules\selenium-webdriver\lib\webdriver\webdriver.js:711:17)
    at Object.<anonymous> (C:\Work\study\selenium\test.js:15:8)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)

Javascript Solutions


Solution 1 - Javascript

I stumbled upon an answer to my question

So to wait for an element to appear we have to:

driver.wait(function () {
    return driver.isElementPresent(webdriver.By.name("username"));
}, timeout);

Solution 2 - Javascript

You don't need a custom function, you can just do this:

let el = await driver.findElement(By.id(`import-file-acqId:${acqId}`));
await driver.wait(until.elementIsVisible(el),100);
await el.sendKeys(file);

See the docs here.

Solution 3 - Javascript

You can register a listener on webdriver.wait by using then()

driver.wait(until.elementLocated(By.name('username')), 5 * 1000).then(el => {
	el.sendKeys(username);
});

Solution 4 - Javascript

This is the only thing that is working for me:

const element = By.id('element');
driver.wait(until.elementLocated(element));
const whatElement = driver.findElement(element);
driver.wait(until.elementIsVisible(whatElement), 5000).click();

Solution 5 - Javascript

Try something like this:

function isItThere(driver, element){

	driver.findElement(webdriver.By.id(element)).then(function(webElement) {
			console.log(element + ' exists');
		}, function(err) {
			if (err.state && err.state === 'no such element') {
				console.log(element + ' not found');
			} else {
				webdriver.promise.rejected(err);
			}
		});
}

I adapted it slightly based on what I found here: https://stackoverflow.com/questions/20148857/check-if-element-exists-selenium-javascript-node-js and it worked a charm.

Solution 6 - Javascript

I usually use this way:

 var el = driver.wait(until.elementLocated(By.name('username')));
el.click();

Solution 7 - Javascript

Writing asynchronous function to avoid this problem

(async function() {
  let url = args[0];
  await driver.get(url);
  driver.quit();
})();

Solution 8 - Javascript

I came up with this approach because it maintains the chainable promise syntax so that I can write this: await waitFind(By.id('abc')).click()

const waitFind = (locator) => {
	return driver.findElement(async () => {
		await driver.wait(until.elementLocated(locator));
		return driver.findElement(locator);
	});
}

Solution 9 - Javascript

The main problem is webdriver thinks element is already there, but not yet. I have a solution, ugly but works. After the webdriver think item is there, try to click on. Get an error message:

StaleElementReferenceError: stale element reference: element is not attached to the page document  (Session info: chrome=83.0.4103.106)

No problem, in the loop waiting 500ms, and try to click on again. In my case 5 try is enough, about 2-3 click is success.

async clickonitem( driver, itemname ) {
    const strftime = require('strftime');
    var trycounter = 0;
    var timeout = 500;
    var success;
    do {
      try {
        trycounter++;
        success = true;
        console.log( strftime('%F %T.%L'), "Finding #" + trycounter + " " + itemname );
        var item = await driver.wait( until.elementLocated( By.xpath( '//input[@name="' + itemname +'"]' ) ), 
                        timeout );
        console.log( strftime('%F %T.%L'), "Found or Timeout #" + trycounter );
        //await item.click();
        await driver.wait( item.click(), 
                        timeout );
        console.log( strftime('%F %T.%L'), "Click #" + trycounter + " " + itemname );
      } 
      catch(err) {
        success = false;
        //this.log( "Error #" + trycounter + " " + itemname + "\n" +err );
        this.log( strftime('%F %T.%L'), "Error #" + trycounter + " " + itemname + " waiting: " + timeout );
        await wait( timeout );
        continue;
      }
    
    } while( !success && trycounter < 5 );
}       
        async wait( ms ) {
          return new Promise((resolve) => {
            setTimeout(resolve, ms);
          });
        }
    
clickonitem( driver, "login_button" );

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAnand SunderramanView Question on Stackoverflow
Solution 1 - JavascriptAnand SunderramanView Answer on Stackoverflow
Solution 2 - JavascriptAlexander MillsView Answer on Stackoverflow
Solution 3 - JavascriptJoe CoderView Answer on Stackoverflow
Solution 4 - JavascriptChristopher GriggView Answer on Stackoverflow
Solution 5 - JavascriptQualiTView Answer on Stackoverflow
Solution 6 - JavascriptTadeu RangelView Answer on Stackoverflow
Solution 7 - JavascriptTamilView Answer on Stackoverflow
Solution 8 - JavascriptJames HView Answer on Stackoverflow
Solution 9 - JavascriptSándor KrisztiánView Answer on Stackoverflow