nightwatch icon indicating copy to clipboard operation
nightwatch copied to clipboard

element found by `element.findAll(selector).nth()` cannot be used in old command like `setValue(ele, text)`

Open Terence625 opened this issue 1 year ago • 6 comments

Description of the bug/issue

When I use element.findAll(selector).nth() to find an element, I expect to put this element into the old command like setValue(ele, text), but throw error. It works with element.find(selector)

Steps to reproduce

No response

Sample test

import { NightwatchTests } from "nightwatch";

const home: NightwatchTests = {
  "Google search test": async() => {
    browser.url("https://google.com/ncr").assert.titleEquals("Google");

    const ele1 = browser.element.findAll(by.css("textarea[name=q]")).nth(0);
    await browser.setValue(ele1, "nightwatchjs"); //Fail

    const ele2 = browser.element.find(by.css("textarea[name=q]"));
    await browser.setValue(ele2, "nightwatchjs"); //Success

  },
};

export default home;

Command to run

npx nightwatch nightwatch/google.ts

Verbose Output

- OTHER ERRORS:
  Error
    NoSuchElementError
   An element could not be located on the page using the given search parameters.
    {"error":{},"status":-1,"value":null}

    Try fixing by :
    1. Please inspect the html before the step
    2. Verify if an element with the mentioned selector is present in the DOM tree

    Error location:
    /Users/wenguang.tian/nightwatch-test/nightwatch/google.ts:
    –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
     5 |     browser.url("https://google.com/ncr").assert.titleEquals("Google");
     6 |     const ele = browser.element.findAll(by.css("textarea[name=q]")).nth(0);
     7 |     await browser.setValue(ele, "nightwatchjs"); 
     8 |     browser.debug();
     9 |   },
    –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––

Nightwatch Configuration

No response

Nightwatch.js Version

3.1.0

Node Version

19.9.0

Browser

No response

Operating System

No response

Additional Information

No response

Terence625 avatar Aug 07 '23 06:08 Terence625

@Terence625 That is not the correct way of using it. Instead of using browser.setValue() and passing the element to it, you can directly do:

import { NightwatchTests } from "nightwatch";

const home: NightwatchTests = {
  "Google search test": async () => {
    browser.url("https://google.com/ncr").assert.titleEquals("Google");

    const ele1 = browser.element.findAll(by.css("textarea[name=q]")).nth(0);
    await ele1.setValue("nightwatchjs");

    const ele2 = browser.element.find(by.css("textarea[name=q]"));
    await ele2.setValue("nightwatchjs");

  },
};

export default home;

garg3133 avatar Aug 07 '23 10:08 garg3133

@Terence625 That is not the correct way of using it. Instead of using browser.setValue() and passing the element to it, you can directly do:

import { NightwatchTests } from "nightwatch";

const home: NightwatchTests = {
  "Google search test": async () => {
    browser.url("https://google.com/ncr").assert.titleEquals("Google");

    const ele1 = browser.element.findAll(by.css("textarea[name=q]")).nth(0);
    await ele1.setValue("nightwatchjs");

    const ele2 = browser.element.find(by.css("textarea[name=q]"));
    await ele2.setValue("nightwatchjs");

  },
};

export default home;

@garg3133 yeah, I know that way, just our project used to use V2, and has a lot of old commands like setValue(ele, text), I'm just refactoring the element definition without changing the commands. browser.element.find() works with the old command and the element type matches the command argument type requirement. I assume these new element command will work with the old commands too

Terence625 avatar Aug 07 '23 10:08 Terence625

@Terence625 In that case, you could use await with element.find and element.findAll commands. Awaiting these commands returns a WebElement which can be directly used with other commands.

import { NightwatchTests } from "nightwatch";

const home: NightwatchTests = {
  "Google search test": async() => {
    browser.url("https://google.com/ncr").assert.titleEquals("Google");

    const ele1 = await browser.element.findAll(by.css("textarea[name=q]")).nth(0);
    await browser.setValue(ele1, "nightwatchjs");

    const ele2 = await browser.element.find(by.css("textarea[name=q]"));
    await browser.setValue(ele2, "nightwatchjs");

  },
};

export default home;

While passing the object returned by browser.element.find() to other commands works right now, it is not entirely correct. It will only work with one layer, and if you nest element.find() with other find commands, it won't work.

garg3133 avatar Aug 07 '23 10:08 garg3133

@Terence625 In that case, you could use await with element.find and element.findAll commands. Awaiting these commands returns a WebElement which can be directly used with other commands.

import { NightwatchTests } from "nightwatch";

const home: NightwatchTests = {
  "Google search test": async() => {
    browser.url("https://google.com/ncr").assert.titleEquals("Google");

    const ele1 = await browser.element.findAll(by.css("textarea[name=q]")).nth(0);
    await browser.setValue(ele1, "nightwatchjs");

    const ele2 = await browser.element.find(by.css("textarea[name=q]"));
    await browser.setValue(ele2, "nightwatchjs");

  },
};

export default home;

While passing the object returned by browser.element.find() to other commands works right now, it is not entirely correct. It will only work with one layer, and if you nest element.find() with other find commands, it won't work.

@garg3133 then is this a typescript related bug? Because you can see if passing ScopedElement as the command argument, there is not typescript error Screenshot 2023-08-07 at 8 56 58 pm But passing the WebElement as the command argument, it shows typescript error Screenshot 2023-08-07 at 8 58 57 pm

Terence625 avatar Aug 07 '23 11:08 Terence625

You're right. I think we should be doing both of these things:

  • Allow to pass ScopedElement to older commands (this could be done by checking if a .webElement property exists on ScopedElement instance and if that is an instance of WebElementPromise).
  • Add a fix in TypeScript to allow the use of WebElement in older commands (but first we would need to check if WebElement is actually accepted in all the older commands).

For now, could you try passing the WebElement instance to the older commands and use // @ts-ignore to ignore the TS error on that line for temporary basis, till this is fixed in TS?

garg3133 avatar Aug 17 '23 11:08 garg3133

@garg3133 yes, I'll use @ts-ignore to workaround for now, thanks

Terence625 avatar Aug 17 '23 23:08 Terence625