coach_rob
coach_rob

Reputation: 901

Testing a list of URLs in CasperJS

I'm trying to avoid having 20 or so very small tests that each essentially test that a list of white label sites are up and show some basic information.

I've got a list (array) of sites, their URL, expected title and the name of the site.

I want to iterate the array, going to each URL and validating the title is correct.

Like this:

var x = require('casper').selectXPath;

var white_labels = [
    {
        URL: "http://site1.com",
        Title: "Site 1 Title",
        Name: "Site 1"
    },
    {
        URL: "http://site2.com",
        Title: "Site 2 Title",
        Name: "Site 2"
    }
]

casper.test.begin('White Labels Test Suite', white_labels.length, function suite(test) {
    var urls; 
    var i = -1;

    casper.start(white_labels[0]["URL"], function() {
        urls = white_labels; 
    });

    casper.then(function() {
        this.each(urls, function() {
            i++;  
            this.echo("I: " + i);
            this.thenOpen(urls[i].URL, function() {
                this.echo("URL: " + urls[i].URL);
                test.assertTitle(urls[i].Title, urls[i].Name + " title is correct");
            });
        });
    });

    casper.run(function() {
        test.done();
    });
});

When I run this, each time I print out "I", it is correct...0, then 1.

However, the first test fails because the title it gets back is the title of site 2, not site 1. The 2nd test succeeds.

I'm baffled at this point.

Upvotes: 1

Views: 889

Answers (1)

Artjom B.
Artjom B.

Reputation: 61922

The problem is the variable scope of i. casper.each is essentially a synchronous for loop. Inside of it is the asynchronous casper.thenOpen statement which only schedules an action. It is actually called synchronously and the correct url is passed into it. After the each loop is executed, the casper step queue begins to execute (triggered through casper.run) which includes all then* calls.

The problem is that i is in global and it means that when both of the casper.thenOpen callbacks are finally executed, i is 1 for both of them.

There are different solutions for your case:

  • Fix the index on each iteration to that iteration by exchanging i++; for var j = ++i; and all the other i inside casper.each for j.
  • Use the fact that casper.each injects the iteration item into the iteration and don't use any index at all: this.each(urls, function(self, url) { ... });.
  • The easiest is to just change this.each to this.eachThen so that it itself is stepped and you don't have i-problems.

Finally, see JavaScript closure inside loops – simple practical example and How do JavaScript closures work? for more information.

Upvotes: 4

Related Questions