0

I have been trying for the past 3 days without success to get a function i'm using to get the CSS Selector paths of a list of elements fitting a certain selector, to work as a custom command in Nightwatch JS.

What you give to the command:

browser.getUniqueCssSelector('.note');

What the output should be:

['HTML> BODY> SECTION.upper-container > DIV.notes:nth-child(1) > DIV.note:nth-child(1)',
'HTML> BODY> SECTION.upper-container > DIV.notes:nth-child(1) > DIV.note:nth-child(2)',
'HTML> BODY> SECTION.upper-container > DIV.notes:nth-child(2) > DIV.note:nth-child(1)',
'HTML> BODY> SECTION.bottom-container > DIV.inner > DIV.notes:nth-child(1) > DIV.note'
'HTML> BODY> SECTION.bottom-container > DIV.inner > DIV.notes:nth-child(2) > DIV.note']

And so and so.

I have tried lots of different ways to implement it but with no success, I am fairly new to Async/Await so I have not been having alot of luck and haven't been able to relate the examples on the Nightwatch.js guide (https://nightwatchjs.org/guide/extending-nightwatch/#writing-custom-commands) to my issue.

I have written the following using the code segment found in How to generate unique css selector for DOM element? :

// getUniqueCssSelector.js file
exports.command = async function(selector) {
        var browser = this;
     let result = browser.execute(function (selector) {

            const nodes = Array.from(document.querySelectorAll(selector))
            var nodesSelectors = [];

            nodes.forEach(node => {
                nodesSelectors.push(getCssSelectorShort(node));
            });
            return nodesSelectors;

            
            function getCssSelectorShort(el) {
                let path = [], parent;
                while (parent = el.parentNode) {
                let tag = el.tagName, siblings;
                path.unshift(
                    el.id ? `#${el.id}` : (
                    siblings = parent.children,
                    [].filter.call(siblings, sibling => sibling.tagName === tag).length === 1 ? tag :
                    `${tag}:nth-child(${1+[].indexOf.call(siblings, el)})`
                    )
                );
                el = parent;
                };
                return `${path.join(' > ')}`;
            };
            
            

        }, [selector], function(res) {
            console.log('************INSIDE COMMAND RETURN CALLBACK ');
            return res;

        })

        return result;

    }


From there, I want in my test code which calls this custom command, to be able to await it. This command will have to be called on multiple elements so making it a callback function, while would work, would also put my code in an eternal callback stack which would make it look very ugly, very fast.

ideally, I want the code to become something like this:

// nwtest.js file
 let nodeSelectorsList= await browser.getUniqueCssSelectors('.note');
            console.log(nodeSelectorsList); // Would bring me back the entire set I posted above.

nodeSelectorsList.forEach(async (noteElement)=> {
                 let resultSingle = await browser.element('css selector', noteElement);
                 console.log(resultSingle); // Should print the elements returned individually
             })

Unfortunately, I always get undefined as the fruit of my efforts. :/

I have been wrecking my brain over this for several days up to almost a week and tried both Promise and Event implementations, but this one was abit beyond me so I call on the help of SO. Any help would be massively appreciated!.

TomDGW2
  • 29
  • 6
  • `browser.execute` doesn't return a promise, so it is not necessary to await it. The `res` in the `************INSIDE COMMAND` is it right or no? – Manuel Spigolon Aug 03 '20 at 14:48
  • @ManuelSpigolon Correct, It is a leftover from a promise attempt I did, apologies, I'll edit it out. It does return the correct value inside the `function(res)` callback. I cannot for the life of me figure out how to get it out of there and back to my main file, however. – TomDGW2 Aug 03 '20 at 15:47

1 Answers1

0

I think you can wrap it as a Promise and then await from the outside,

// getUniqueCssSelector.js file

exports.command = function (selector) {
    var browser = this;
    return new Promise(function (resolve, reject) {

        browser.execute(function (selector) {

            const nodes = Array.from(document.querySelectorAll(selector))
            var nodesSelectors = [];

            nodes.forEach(node => {
                nodesSelectors.push(getCssSelectorShort(node));
            });
            return nodesSelectors;


            function getCssSelectorShort(el) {
                let path = [],
                    parent;
                while (parent = el.parentNode) {
                    let tag = el.tagName,
                        siblings;
                    path.unshift(
                        el.id ? `#${el.id}` : (
                            siblings = parent.children,
                            [].filter.call(siblings, sibling => sibling.tagName === tag).length === 1 ? tag :
                            `${tag}:nth-child(${1+[].indexOf.call(siblings, el)})`
                        )
                    );
                    el = parent;
                };
                return `${path.join(' > ')}`;
            };



        }, [selector], function (res) {
            resolve(res);
        });
    });
}

// nwtest.js file

let nodeSelectorsList = await browser.getUniqueCssSelectors('.note');
console.log(nodeSelectorsList); // Would bring me back the entire set I posted above.

nodeSelectorsList.forEach(async (noteElement) => {
    let resultSingle = await browser.element('css selector', noteElement);
    console.log(resultSingle); // Should print the elements returned individually
})
Ron Yaari
  • 231
  • 3
  • 10
  • `var browser = this;` should be execute out of the `new Promise` or it will be undefined – Manuel Spigolon Aug 04 '20 at 10:30
  • 1
    Thanks! unfortunately I already tried it and it did not change anything, it might be something to do with how Nightwatch.js handles the exporting, but even as the `resolve` sends the correct values I receive an `undefined` on my file. – TomDGW2 Aug 05 '20 at 06:19