61

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)
1
  • Is your site handling ajax requests? If so have you thought of using javascriptexecutor class in java to check node.js flags for the request? Commented Sep 10, 2014 at 1:23

10 Answers 10

59

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);
Sign up to request clarification or add additional context in comments.

4 Comments

The isElementPresent function has been deprecated in 3.0 so this won't work in newer versions.
@jmreicha can you shed light on what works in the newer versions ?
@AnandSunderraman The answer below using the elementLocated function worked for me.
@jmreicha elementLocated intercepted when spinner is loading and it tries to click button in my case!
47

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.

4 Comments

This is an up to date and idiomatic solution that works. Should be voted much higher!
Doesn't work if element is not in the DOM.
@БранкоПејић I assume it throws an error if el is not found? you tell us bro
@AlexanderMills Yes it will throw a TimeoutError on the first line of code if the element does not exist in the DOM at the expiration of WebDriver's implicit timeout. If the element is found before the timeout, the next line of code will then wait an additional (explicit) 100 milliseconds for the element to become visible. You could instead have a single, explicit timeout on the visibility predicate by passing the return value of until.elementIsVisible(...) to driver.wait() similar to how this answer does.
44

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);
});

6 Comments

Could you explain why you have 5 * 1000 vs 5000? (btw, I'm not the downvoter... :) )
This is a common pattern when dealing with milliseconds. Many devs prefer the clarity indicating that the measurement is in milliseconds.
This one worked for me, remember to const {until} = require('selenium-webdriver'); before
Note: I wanted to click on a button when it is displayed but to make this solution work, I had to use the sleep function inside the then method before clicking on it, for example by waiting 1 second after it is displayed: driver.sleep(1000).then(function() {el.click();});. You will also have less chance to be detected as a bot.
@baptx Ideally you want the automation to run as fast as possible without explicit waits. You could try until.elementIsVisible() instead or call driver.wait() and pass in a custom function. See docs. Avoiding bot detection in Selenium is quite a bit more involved, as you also have to change the User Agent and other settings.
|
5

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();

Comments

3

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: Check if element exists - selenium / javascript / node-js and it worked a charm.

Comments

3

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);
    });
}

2 Comments

Can you be more precise? How does this correct the error encountered in the question?
@Alexandre The answer to the question is to wait for the element to appear. The other answers propose various solutions to do this, but this one has the benefit of retaining the proxy element returned from findElement. Otherwise, the returned object would be a promise lacking the click() and sendKeys() proxy methods.
2

I usually use this way:

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

Comments

1

Writing asynchronous function to avoid this problem

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

Comments

0

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" );

Comments

-1

await driver.wait(until.elementLocated(By.className('searchServiceBtn')), 10000); const searchButton = await driver.findElement(By.className('searchServiceBtn'));

    // Wait for the button to be clickable
    await driver.wait(until.elementIsVisible(searchButton), 10000);
    await driver.wait(until.elementIsClickable(searchButton), 10000);
    
    // Click the search button
    await searchButton.click();
    console.log('Search button clicked.');

    // Wait for the search section to be located
    await driver.wait(until.elementLocated(By.id('servicelistSearch-form-wrp')), 10000);
    
    // Check if the search section is displayed
    const isDisplayed = await driver.findElement(By.id('servicelistSearch-form-wrp')).isDisplayed();
    console.log(`Search section is ${isDisplayed ? 'open' : 'closed'}`);
} catch (error) {
    console.error('Error occurred:', error);

I am not able to automate the things with this code

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.