UnhandledPromiseRejectionWarning: NoSuchElementError: no such element: Unable to locate element:

1,370 views
Skip to first unread message

siva prasad

unread,
Sep 3, 2018, 2:34:30 AM9/3/18
to nodejs
I am fairly new to the JS scripting. 
Started using JS for end-to-end test case automation.

I am facing an issue here. 

Issue: 
UnhandledPromiseRejectionWarning: NoSuchElementError: no such element: Unable to locate element: {"method":"link text","selector":"Business Continuity"}
  (Session info: chrome=68.0.3440.106)
  (Driver info: chromedriver=2.35.528157 (4429ca2590d6988c0745c24c8858745aaaec01ef),platform=Mac OS X 10.13.2 x86_64)
    at Object.checkLegacyResponse (/Users/sivaprasad/node_modules/selenium-webdriver/lib/error.js:585:15)
    at parseHttpResponse (/Users/sivaprasad/node_modules/selenium-webdriver/lib/http.js:533:13)
    at Executor.execute (/Users/sivaprasad/node_modules/selenium-webdriver/lib/http.js:468:26)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
(node:25855) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:25855) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Code:

driver.findElement(By.id('username')).sendKeys('xxx');
driver.findElement(By.id('password')).sendKeys('xxx');
driver.findElement(By.id('submit')).click();
driver.findElement(By.linkText('Business Continuity')).click();

I know there is something called Promise handling. Just that I am not able to get my head around it. Someone help me. 

Mikkel Wilson

unread,
Sep 4, 2018, 6:30:08 PM9/4/18
to nodejs
Siva, 

I'm not sure exactly what front-end testing framework you're using here, but there are two ways to handle the promise rejection you're seeing. First is to just wrap this whole block in a try/catch.

try {

  driver
.findElement(By.id('username')).sendKeys('xxx');
  driver
.findElement(By.id('password')).sendKeys('xxx');
  driver
.findElement(By.id('submit')).click();
  driver
.findElement(By.linkText('Business Continuity')).click();
} catch (err){
  console
.log('error', err);
}

This will handle any Promise rejections from any of these findElement() calls. If you want to be more granular is you're error handling, you can 'catch' any of the Promise rejections individually.


driver
.findElement(By.id('username')).sendKeys('xxx');
driver
.findElement(By.id('password')).sendKeys('xxx');
driver
.findElement(By.id('submit')).click();
driver
.findElement(By.linkText('Business Continuity')).click()

 
.catch( (err) => {
    console
.log('error', err);
 
});

It should also be noted that these findElement calls are not guaranteed to run in sequence. This may also be a source of trouble for you since the 'Business Continuity' link may not be there before the 'submit' button is clicked.
Since the findElement functions all return Promises, making sure they run in order is quite straight forward using async/await.

it('tests login sequence', async function() {
  await driver
.findElement(By.id('username')).sendKeys('xxx');
  await driver
.findElement(By.id('password')).sendKeys('xxx');
  await driver
.findElement(By.id('submit')).click();
  await driver
.findElement(By.linkText('Business Continuity')).click()
   
.catch( (err) => {
      console
.log('error', err);
   
});
}


One last thought: When writing tests, it's often helpful to *not* catch errors and let the test crash. This leaves a more helpful stack trace than the error Object that the caught error provides.

HTH,
Mikkel

siva prasad

unread,
Sep 5, 2018, 2:14:56 AM9/5/18
to nodejs
Thanks a ton Mikkel. That was really helpful.

I have one more question though. 
I come from a Java background. 

We have something like this in Java for Selenium.

findElement(By.xpath("//xxx")).isEmpty()

Is there anything similar to this in JS for selenium?

I have done a bit of research but in vain.
It would be really helpful.

Mikkel Wilson

unread,
Sep 5, 2018, 3:44:23 PM9/5/18
to nodejs
Siva, 

So, this is the part about asynchronous code that takes a moment to get your head around. findElement is returning a Promise, not the Element object as you would expect in Java. To get the result of the findElement function you need to wait until it's complete evaluating the function. You can do this in two ways:

// assuming e.isEmpty() is a method of the returned element...
findElement
(By.xpath("//xxx")).then( (e) => {
 
e.isEmpty();
});

Or using async/await :

let e = await findElement(By.xpath("//xxx"));
e
.isEmpty();

HTH,
Mikkel

siva prasad

unread,
Sep 6, 2018, 12:05:35 PM9/6/18
to nodejs
I have tried this too. 

This is my code block: 

var elem = dvr.findElement(By.xpath("//tr[contains(.,'xxx')]"));
if (elem.isEmpty()){
console.log('Not displayed' + i);
//count++;
dvr.findElement(By.xpath("//a[contains(.,'»')]"));
}
else {
console.log('Displayed in page' + i);
dvr.findElement(By.xpath("//tr[contains(.,'xxx')]/td[3]/a"));
dvr.switchTo().alert().accept();
}


This is the error I get:
elem.isEmpty is not a function

Why is this happening?


On Monday, 3 September 2018 12:04:30 UTC+5:30, siva prasad wrote:

Mikkel Wilson

unread,
Sep 6, 2018, 2:36:01 PM9/6/18
to nodejs

You're missing an important keyword, 'await'. I'll try to explain.

var elem = await dvr.findElement(By.xpath("//tr[contains(.,'xxx')]"));

This will let the findElement function finish executing and return the element object, then assign it to 'elem'. Without the 'await' keyword, 'elem' is a Promise object which doesn't have a method called 'isEmpty'. 

It's probably instructive to see what a Promise'd function looks like in practice. Let's write a function that waits 1 second, and returns the sum of its parameters.

function addUs(a, b){
 
return new Promise( (accept) => {
    setTimeout
( () => {
      accept
(a+b);
   
}, 1000);
 
});
}

The function returns right away, but it's not actually done executing until the accept() method is called. The findElement call does much the same. To use this function, let's use the async/await pattern:

async function runAddUs(){
  console
.log(addUs(1, 2));       // outputs Promise object; not super helpful
  console
.log(await addUs(3, 4)); // outputs '7'; that's what we want
}
runAddUs
();

You'll notice that the first addUs call returns immediately and the console.log output will happen immediately. It will display information about the Promise object, but the addUs call isn't actually complete (the Promise is in 'pending' state). Then 1 second later the second console.log will output the number 7. Your findElement code above is more like the first addUs(1, 2) call here. 'elem' is assigned the value of the Promise object, not the value of the result of findElement. In JavaScript, the functions are *executed* in order, but they may not finish running in that same order. This is the nature of asynchronous vs. procedural programming. It's quite possible to write asynchronous code in many languages, but with JavaScript it's the default.

Best,
Mikkel
Oblivious.io
Reply all
Reply to author
Forward
0 new messages