1

I am trying to write a script that will do the following:

  1. load a website and take a screenshot
  2. select a random <a> tag from all <a> tags with the same class name
  3. click the link
  4. wait for the new page to load
  5. capture the second screenshot

I'm stuck at selecting a random <a> element and clicking it. Can anyone help me out please? It's my first day with casperjs. Here's what i have so far:

var casper = require('casper').create({
     verbose: true,
     logLevel: 'debug',
     userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22',
     pageSettings: {}
});
casper.options.viewportSize = {width: 1600, height: 950};


casper.start('http://www.myurl.com/', function() {
    this.echo(this.getTitle());
    this.capture('home.png');
});

casper.then(function() {
        this.echo("Second test");
        var random = Math.floor(Math.random() * document.querySelector(".showall").length);
        var clicker = document.querySelector('.showall').eq(random);
        this.mouseEvent('click', clicker);

        this.wait(5000, function() {
            this.capture('second.png');
       });

});

casper.run();
Wojtek Zeglin
  • 106
  • 1
  • 10

2 Answers2

2

You cannot use the DOM functions outside of the page context. You have to use casper.evaluate for that. Keep in mind that evaluate is sandboxed and you can only pass primitive objects to and from page context. DOM elements are not primitive objects, so you cannot pass them outside into the casper context. document means nothing outside of evaluate.

From the docs:

Note: The arguments and the return value to the evaluate function must be a simple primitive object. The rule of thumb: if it can be serialized via JSON, then it is fine.

You will have to click the link with DOM methods inside of the page context. If the simple clicker.click() doesn't work, you have to use something like this in the page context.

If the link is actually a link which immediately navigates to the page, you can removed this.wait. CasperJS can sense on its own if a page is navigated to, so you can just use another casper.then step.

By the way eq is jQuery syntax, but the result of querySelector is not a jQuery object, it is a DOM element. If you want to select a random element then you have to use querySelectorAll and then select one of them:

var clicker = document.querySelectorAll('.showall')[random];

instead.

Complete solution:

casper.then(function() {
    this.echo("Second test");
    this.evaluate(function() {
        var elements = document.querySelectorAll(".showall");
        var random = Math.floor(Math.random() * elements.length);
        var clicker = elements[random];

        // either this type of click
        clicker.click();
        // or the following
        var ev = document.createEvent("MouseEvent");
        ev.initMouseEvent(
            "click", true, true, window, null, 0, 0, 0, 0, 
            false, false, false, false, 0, null
        );
        clicker.dispatchEvent(ev);
    });
});
casper.then(function() {
    this.capture('second.png');
});
Community
  • 1
  • 1
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Thank you very much for your help, Artjom. However, i noticed that none of the code from within `this.evaluate(function()` gets executed at all. What can be causing this? I had this problem before when i was trying to use `evaluate` myself. Is it a configuration issue..? Doesn't seem like it, but i'm not entirely sure. – Wojtek Zeglin Sep 15 '14 at 13:48
  • It is executed, but you don't necessarily see the results. You should register to the [`remote.message`](http://docs.casperjs.org/en/latest/events-filters.html#remote-message) and [`page.error`](http://docs.casperjs.org/en/latest/events-filters.html#page-error) events. – Artjom B. Sep 15 '14 at 13:52
  • Indeed, registering page.error helped and displayed the following error: `Error: TypeError: 'undefined' is not an object (evaluating 'clicker.click')`. – Wojtek Zeglin Sep 16 '14 at 07:20
  • I completely missed that `querySelector` should be `querySelectorAll`. I added a reason. Please accept the answer if it helps you. – Artjom B. Sep 16 '14 at 07:27
  • It still does not work, but i will find a solution myself. Thanks for all your help. – Wojtek Zeglin Sep 16 '14 at 08:02
  • Have you tried both types of clicking? The other thing that you can try, is to remove the `this.wait(5000,` with `this.then(`, because casper will sense that there is a navigation to another page and wait for you until it is loaded. Also, you don't have to [accept](http://meta.stackexchange.com/a/5235) the answer now. – Artjom B. Sep 16 '14 at 10:42
1

I didn't manage to get Artjom's solution working. I think i'm using querySelectorAll in a wrong way (not sure why). I found a workaround.

function getElem() {
    var arr = document.querySelectorAll('a.showall');
    return Array.prototype.map.call(arr, function(elem) {
        return elem.getAttribute('href');
    });
}

casper.then(function() {

        var elem_arr = this.evaluate(getElem);
        //this.echo(elem_arr);
        this.echo(elem_arr.length);

        for (var index = 0; index < elem_arr.length; index++) {
            this.echo(elem_arr[index]);
        }

        var random = Math.floor(Math.random() * elem_arr.length);
        this.echo(random);

        var clicker = elem_arr[random];
        this.echo("Random link: " + clicker);

        casper.open(clicker).then(function() {
            this.echo('Got it! You navigated to a random link subpage.');
        });


});

The trick is to create an array of links from all a.showall elements and then select a random link and open it.

Wojtek Zeglin
  • 106
  • 1
  • 10