0

I have started using Selenium. When I want to wait for the element to be loaded, it throws this error:

    (node:8472) UnhandledPromiseRejectionWarning: NoSuchElementError: no such element: Unable to locate element: {"method":"xpath","selector":"//a[@value='view all']"}
  (Session info: chrome=70.0.3538.110)
  (Driver info: chromedriver=2.45.615291 (ec3682e3c9061c10f26ea9e5cdcf3c53f3f74387),platform=Windows NT 10.0.17134 x86_64)
    at Object.checkLegacyResponse (C:\Users\Ben Levy\Desktop\bot\node_modules\selenium-webdriver\lib\error.js:585:15)
    at parseHttpResponse (C:\Users\Ben Levy\Desktop\bot\node_modules\selenium-webdriver\lib\http.js:533:13)
    at Executor.execute (C:\Users\Ben Levy\Desktop\bot\node_modules\selenium-webdriver\lib\http.js:468:26)
    at process._tickCallback (internal/process/next_tick.js:68:7)
(node:8472) 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:8472) [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.

Here is the code I am trying:

const {Builder, By, Key, until} = require('selenium-webdriver'),
  app = require('express'),
  express = app();

let driver = new Builder().forBrowser('chrome').build();

driver.get('https://supremenewyork.com');

driver.findElement(By.className('shop_link')).click();

driver.wait(()=>{
  driver.findElement(By.xpath("//a[@value='view all']").isDisplayed());
});
//Bugged code starts below.
let all = driver.findElement(By.xpath("//a[@value='view all']"));

driver.promise.filter(all, (element)=>{
  return element.isDisplayed();
}).then((element)=>{
  element.click();
});
//End of bugged code.

Please tell me what I am doing wrong/a better method for waiting until the element is loaded. Thanks!


This is the revised code:

const {Builder, By, Key, until} = require('selenium-webdriver'),
  app = require('express'),
  express = app();
  driver = new Builder().forBrowser('chrome').build();

driver.get('https://supremenewyork.com');

driver.findElement(By.className('shop_link')).click();
async function main(){
  let all = await driver.findElements(By.xpath("//a[@value='view all']")),
  element,
  visible;

  for(element of elements){
    visible = await element.isDisplayed();
    if (visible) {
    element.click();
    }
  }
};
user9985211
  • 221
  • 3
  • 12

2 Answers2

0

I figured it out! I used Selenium wait docs

The code is:

const { Builder, By, Key, until } = require('selenium-webdriver');

async function main() {
    let driver = new Builder().forBrowser('chrome').build();

    driver.get('https://supremenewyork.com');

    driver.findElement(By.className('shop_link')).click();
    let element =
      await driver.wait(until.elementLocated(By.xpath("//*[contains(@href,'http://www.supremenewyork.com/shop/all')]", 10000)));
    await element.click();
}

main();
user9985211
  • 221
  • 3
  • 12
-2

First of all: you should learn about the concept of async programming in general, Promises, ECMAScript 2016 and ECMAScript 2017 features:

Further, you should understand the WebDriver interface. You might take a look into the code:

Now ...

Arrow functions and Selenium interface

//Bugged code starts below.

No, it starts a couple of lines earlier.

driver.wait(()=>{
  driver.findElement(By.xpath("//a[@value='view all']").isDisplayed());
});

This is wrong in two aspects:

  1. An arrow function with a block requires a return statement
  2. isDisplayed() is a function on the WebElementPromise returned by findElement(). You were calling it on the selector object.

This is correct:

driver.wait(() => {
  return driver.findElement(By.xpath("//a[@value='view all']")).isDisplayed();
});

Async functions

Now, wait() is async code. It returns a Promise and as such, it has to be awaited or handled via then(), otherwise the following statements are executed immediately after waiting indefinitely for an element visibility.

Better:

await driver.wait(() => {
  return driver.findElement(By.xpath("//a[@value='view all']")).isDisplayed();
});

Now, await can only be used in an async function, thus you've to wrap all of your code in an async function, for example:

async main() {
  // ...
  await driver.wait(...);
  // wait() condition fulfilled, code continues
  driver.findElement(...).click();
  // ...
}

main();

Selenium interface and array of Promises

let all = driver.findElement(By.xpath("//a[@value='view all']"));

From the naming and the selector one can assume that you expect multiple elements. Though as the name implies findElement() will only find one element, the first. Use findElements(). This will resolve with an array of WebElements.

There's no driver.promise.

To make it short, the final block might look like this:

    // as with earlier examples, findElements() is async (returns a Prmomise),
    // thus you've to await the result
    let elements = await driver.findElements(By.xpath("//a[@value='view all']")),
        element,
        visible;

    for (element of elements) {
        // isDisplayed() is async (returns a Promise),
        // thus you've to await the result
        visible = await element.isDisplayed();

        if (visible) {
            element.click();
        }
    }

This shall give you some direction what to study.

With Chromium or Chrome you can debug your Node.js code

node --debug-brk --inspect bot.js

See this answer and Google Developers for some guidance.


Complete code

async function main() {
    const { Builder, By, Key, until } = require('selenium-webdriver');

    let driver = new Builder().forBrowser('chrome').build();

    driver.get('https://supremenewyork.com');

    driver.findElement(By.className('shop_link')).click();

    await driver.wait(() => {
       return driver.findElement(By.xpath("//a[@value='view all']")).isDisplayed();
    });

    let elements = await driver.findElements(By.xpath("//a[@value='view all']")),
        element,
        visible;

    for (element of elements) {
        visible = await element.isDisplayed();

        if (visible) {
            element.click();
        }
    }
}

main();
try-catch-finally
  • 6,720
  • 6
  • 37
  • 64