Javascript Can't Pass Vars/Fn To .evaluate() Scope (NightmareJS)

Viewed 264

I'm trying to pass a variable to .evaluate so I can use them in the scope of the web page but I can't get it to work.

await nightmare.evaluate(function() {
        let links = document.querySelectorAll('div.fsl a');
        return Array.prototype.map.call(links, function(e) {
            return e.getAttribute('href');
        });
    })

    .evaluate(function(result) {
        for(var i = 0; i < result.length; i++) {
            var matchResult = result[i].match(/.com\/(.*?)\?fref/);
            if (matchResult) {
                console.log(matchResult[1]);
            }
        }
    });

For this I get Cannot read property 'match' of undefined. I then tried:

const evaluated = await nightmare.evaluate(function() {
        let links = document.querySelectorAll('div.fsl a');
        return Array.prototype.map.call(links, function(e) {
            return e.getAttribute('href');
        });
    });

    await nightmare.evaluate(function(evaluated) {
        for(var i = 0; i < evaluated.length; i++) {
            var matchResult = evaluated[i].match(/.com\/(.*?)\?fref/);
            if (matchResult) {
                console.log(matchResult[1]);
            }
        }
    });

With the same result. I then tried:

const evaluated = await nightmare.evaluate(function() {
        let links = document.querySelectorAll('div.fsl a');
        return Array.prototype.map.call(links, function(e) {
            return e.getAttribute('href');
        });
    });

for(var i = 0; i < evaluated.length; i++){
        var matchResult = evaluated[i].match(/.com\/(.*?)\?fref/);
        if(matchResult) {
            console.log(matchResult[1]);
            await nightmare.evaluate(function(matchResult) {
                return document.body.innerHTML += '<a href="https://www.example.com/'+matchResult[0]+'">'+matchResult[0]+'</a>';
            });
            await nightmare.click('a[href="https://www.example.com/'+matchResult[0]+'"]');
            await nightmare.wait(5000);
        }
    }
await nightmare.end();

For this, the first iteration of the loop is executed and matchResult[1] is logged to the console. I then get Error: Evaluation timed out after 30000msec. Are you calling done() or resolving your promises?.

I also tried something like this:

await nightmare.evaluate(function() {
        let links = document.querySelectorAll('div.fsl a');
        return Array.prototype.map.call(links, function(e) {
            return e.getAttribute('href');
        });
    }).then(function(users){

    }).end();

Which does pass the return to .then() but how do I pass the array into the next evaluate? And now this is the last thing I can think of but doesn't work:

await nightmare.evaluate(function() {
        let links = document.querySelectorAll('div.fsl a');
        return Array.prototype.map.call(links, function(e) {
            return e.getAttribute('href');
        });
    }).evaluate((users) => {
        console.log(users);
    }).end();

I found a solution here but it returns undefined for me. I also found this which talks about a similar thing in PhantomJS but haven't come up with a workable code yet.

1 Answers

I have found the answer. It seems as though half my attempts were probably working and I just didn't realize it. Within the scope of .evaluate(), we're in the browser scope (As intended) but I wasn't considering that I can't log to the console from that scope. Specifically, I can't log to my console. If I run console.log from that scope, it's logging to the browser console. The following code works:

    var foo = "stuff";
    var bar = "stuff for the remote page";

    var result = await nightmare.evaluate(function(bar2) {
        // this function executes in the remote context
        // it has access to the DOM, remote libraries, and args you pass in
        console.log("bla "+bar2); // this will not output
        // but not to outer-scope vars
        return typeof foo + " " + bar2;
    }, bar);
    console.log(result); // this logs bar2!

I think this solves it. It might be a bit tricky to debug what's going on in the document scope but certainly possible.

Related