nightwatchjs / nightwatch

Integrated end-to-end testing framework written in Node.js and using W3C Webdriver API. Developed at @browserstack
https://nightwatchjs.org
MIT License
11.83k stars 1.32k forks source link

.elements() returns different result for geckodriver #2343

Closed KoBe321 closed 4 years ago

KoBe321 commented 4 years ago

chromedriver vs geckodriver: different return types

.elements() returns different type of result

command.js

```js .elements("css selector", ".inputField", (inputFields: any) => { console.log("input fields", inputFields); }) ```

Run with command

$ npx nightwatch --env firefox --test js\tests\tev2\tev2_style.js

Verbose output

console.log

```txt // chromedriver { sessionId: '5d160b0b2808e96e181a67e347760c51', status: 0, value: [{ ELEMENT: '0.31906899492724383-12' }, { ELEMENT: '0.31906899492724383-13' }, ] } // geckodriver { value: [{ 'element-6066-11e4-a52e-4f735466cecf': 'f53da795-6871-4 }, { 'element-6066-11e4-a52e-4f735466cecf': '38acacec-17ae-4 }, ] } ```

Configuration

nightwatch.json

```js module.exports = { // An array of folders (excluding subfolders) where your tests are located; // if this is not specified, the test source must be passed as the second argument to the test runner. src_folders: ["js/tests"], // See https://nightwatchjs.org/guide/working-with-page-objects/ page_objects_path: 'js/page_objects', // See https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-commands custom_commands_path: 'js/custom_commands', // See https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-assertions custom_assertions_path: '', // See https://nightwatchjs.org/guide/#external-globals globals_path: '', webdriver: {}, // Other environemnts inherit from the default environment. test_settings: { default: { globals: { }, disable_error_log: false, launch_url: 'https://nightwatchjs.org', screenshots: { enabled: false, path: 'screens', on_failure: true }, desiredCapabilities: { browserName: 'firefox' }, webdriver: { start_process: true, server_path: (Services.geckodriver ? Services.geckodriver.path : '') } }, firefox: { desiredCapabilities: { browserName: 'firefox', alwaysMatch: { // Enable this if you encounter unexpected SSL certificate errors in Firefox acceptInsecureCerts: true, 'moz:firefoxOptions': { args: [ // '-headless', // '-verbose' ], } } }, webdriver: { start_process: true, port: 4444, server_path: (Services.geckodriver ? Services.geckodriver.path : ''), cli_args: [ // very verbose geckodriver logs // '-vv' ] } }, chrome: { desiredCapabilities: { browserName: 'chrome', chromeOptions: { // This tells Chromedriver to run using the legacy JSONWire protocol (not required in Chrome 78) //w3c: false, // More info on Chromedriver: https://sites.google.com/a/chromium.org/chromedriver/ args: [ //'--no-sandbox', '--ignore-certificate-errors', //'--allow-insecure-localhost', //'--headless' ] } }, webdriver: { start_process: true, port: 9515, server_path: (Services.chromedriver ? Services.chromedriver.path : ''), cli_args: [ // --verbose ] } }, } }; ```

Your Environment

Executable Version
nightwatch --version 1.3.4
npm --version 6.13.4
node --version v12.14.1
Browser driver Version
chromedriver 80.0.1
geckodriver 1.19.1
OS Version
Windows 10 1909
beatfactor commented 4 years ago

So is this a problem? We can't control what the browser drivers return, so I'm not sure what you're expecting us to do here?

KoBe321 commented 4 years ago

The problem is that other functions use the web element id. With chromedriver I can use that like this:

 for (let inputField of inputFields.value) {
      browser.elementIdElement(inputField.ELEMENT, "css selector", "label", (labels: any) => {
})}

But in case of geckodriver, there's no ELEMENT property. So how should I use elementIdElement() consistently?

EDIT I know I could get the values of the properties of the objects in value regardless of the driver, but maybe I just missed something in understanding how Nightwatch works?

beatfactor commented 4 years ago

elementIdElement is rarely used directly, it's mostly for when working with page objects. https://nightwatchjs.org/guide/working-with-page-objects/

KoBe321 commented 4 years ago

What I'm trying to do is to get all label-input pairs, find a label by its text then find the corresponding input. From my understanding, if I want to somehow filter an element using .elements() then from that point I have to use web element id-s, or not?

       .elements("css selector", ".inputField", (inputFields: any) => {
                for (let inputField of inputFields.value) {
                    browser.elementIdElement(inputField.ELEMENT, "css selector", "label", (labels: any) => {
                        if (labels.value.ELEMENT) {
                            browser.elementIdAttribute(labels.value.ELEMENT, "innerText", (text: any) => {
                                if (text.value === setting.label) {
                                    browser.elementIdElement(inputField.ELEMENT, "css selector", "input", (input: any) => {                                     
                                        browser.elementIdValue(input.value.ELEMENT, setting.value, () => {
                                            console.log("--- setSetting ened");
                                            this.emit('complete');
                                        })
                                    });
                                }
                            })
                        }

                    });
                }

            })
beatfactor commented 4 years ago

You should ask this in the Mailing List. I would also suggest using ES6 await instead of those callbacks, the code is not very readable in this form.

I'm closing this since the GitHub Issues list is meant for submitting bugs and feature requests.

KoBe321 commented 4 years ago

To clarify things, this is indeed not an issue. My whole problem started when I wanted to find an input element based on its corresponding label. After googling around I found answers like this. Many other SO posts can be found where the code relies on the ELEMENT property but it's not stated anywhere that it's driver/browser specific. However I was able to solve my problem by using xpath selectors and not css. E.g.:

    browser
      .url("http://localhost:5500/")
      .useXpath()
      .assert.value('//div[label/text() = "two"]/input', "second input")
      .end();

This was also helpful.