Testing with CasperJS - Part 2

Reading time ~4 minutes

In our last post we have seen how to install CasperJS and how to make a very simple script. In this post we will make more interesting stuff.

In this tutorial we will see how to catch any error in our web page, whether it be the lack of an image or script or an error in javascript code.

So, lets start forcing these errors. Go to your _layout.cshtml page (or other page if you are not using ASP.NET MVC) and add this line to try to load a non existing script.

<script type="text/javascript" src="js/fake.js"></script>

In the same page, find where the logo link is created

<p class="site-title">@Html.ActionLink("your logo here", "Index", "Home")</p>

and change it for a non existing image, like this:

<a href="http://localhost:8000">
    <img src="~/Content/Images/logo.png"></img>
</a>

Now, go to the site.css file, and add this line to .body

background: url("../Images/noExists.png") no-repeat;

And finally, create a script in Scripts folder with this content and name it error.js

function throwError() {
    throw new Error();
}

throwError();

And add it at the end of your _layout.cshtml page

<script src="~/Scripts/error.js"></script>

If we load the page and take a look at the network tab of the developer tools we could see the errors on resource loading.

[caption id="attachment_51" align="alignnone" width="1239"]error loading resourcees error loading resourcees[/caption]

And taking a look at the errors tab, we could see more errors

[caption id="attachment_52" align="alignnone" width="669"]error loading resources error loading resources[/caption]

So, let's try to catch these errors.

Put this code between the casper.start call and the casper.run call.

casper.start('http://localhost:8000/', function () {
    if (this.resourceExists('fake.js')) {
        this.echo('fake.js is downloaded');
    } else {
        this.echo('fake.js is not downloaded');
    }

    if (this.resourceExists('error.js')) {
        this.echo('error.js is downloaded');
    } else {
        this.echo('error.js is not downloaded');
    }
});

With this code we are asking CasperJS whether fake.js and error.js are downloaded. As a result we should have this output:

[caption id="attachment_56" align="alignnone" width="675"]result result[/caption]

That's great! Error.js exists in our project but fake.js don't. So the result is the expected.

Could we do the same with the images? Yes, of course. Replace the previous code with this one:

casper.start('http://localhost:8000/', function () {
    if (this.resourceExists('logo.png')) {
        this.echo('logo.js is downloaded');
    } else {
        this.echo('logo.js is not downloaded');
    }
});

And we should have this output:

[caption id="attachment_57" align="alignnone" width="676"]result result[/caption]

Ok, that's great but it's not reasonable to have all the resources in our web hardcoded in the script. Let's try to generalize a little bit more. Let's start with the scripts:

var scriptsArray = [];

function getScripts() {
    var scripts = document.querySelectorAll('script[src]');
    return Array.prototype.map.call(scripts, function(e) {
        return e.getAttribute('src');
    });
};

casper.start('http://localhost:8000/', function () {
    scriptsArray = this.evaluate(getScripts);
    var self = this;
    scriptsArray.forEach(function(item) {
        if (self.resourceExists(item)) {
            self.echo(item + ' loaded');
        } else {
            self.echo(item + ' not loaded', 'ERROR');
        }
    });
});

In this script we are retrieving all the page's scripts using a css selector. To do this we have to use the evaluate function. With the array of scripts we could call the resourceExists function to know whether the resource is loaded or not.

Executing the script we should have this result:

[caption id="attachment_58" align="alignnone" width="675"]result result[/caption]

And yes, we could do the same for the images, changing the css selector a little bit:

var imagesArray = [];

function getImages() {
    var scripts = document.querySelectorAll('img[src]');
    return Array.prototype.map.call(scripts, function (e) {
        return e.getAttribute('src');
    });
};

casper.start('http://localhost:8000/', function () {
    imagesArray = this.evaluate(getImages);
    var self = this;
    imagesArray.forEach(function (item) {
        if (self.resourceExists(item)) {
            self.echo(item + ' loaded');
        } else {
            var message = item + ' not loaded';
            self.echo(message, 'ERROR');
        }
    });
});

And the output:

[caption id="attachment_59" align="alignnone" width="675"]result result[/caption]

Could be a good idea to take a snapshot if an image is not loaded. We could do this using the capture function:

casper.start('http://localhost:8000/', function () {
    imagesArray = this.evaluate(getImages);
    var self = this;
    var isThereAnError = false;
    imagesArray.forEach(function (item) {
        if (self.resourceExists(item)) {
            self.echo(item + ' loaded');
        } else {
            var message = item + ' not loaded';
            self.echo(message, 'ERROR');
            self.isThereAnError = true;
        }
    });

    if (this.isThereAnError == true) {
        casper.capture('noimages.png');
    }
});

The output will be the same as before, but if you take a look at the script folder you will see that now there is an image in it:

[caption id="attachment_61" align="alignnone" width="826"]no images no images[/caption]

And if you open the image, you will see the web page rendered.

[caption id="attachment_62" align="alignnone" width="1024"]page rendered page rendered[/caption]

But we have a problem. We don't have any notices about the image set in body's background that doesn't exist. We have to try another approach to catch this error. It would be great to catch any 404 error on resources loading, and CasperJS allows us to do.

Change the previous function by this one:

casper.on("resource.received", function(request) {
    if (request.status === 404) {
        this.echo('Resource Not Found:' + request.url);
    }
});

casper.start('http://localhost:8000/', function () {});

Now, we are subscribing to the resource.received event, and looking at the status code we could know whether the resource is loaded or not.

If we take a look at the output we could see that an error is shown for the image inside the css too.

[caption id="attachment_63" align="alignnone" width="675"]catching 404 errors catching 404 errors[/caption]

 

Ok, now we have a generic way to be aware of errors when loading resources. Now we need to know when a javascript error happens. To do that we are going to use the events too, but now we are going to subscribe to page.error event:

casper.on("page.error", function (msg, trace) {
    this.echo("Error:    " + msg, "ERROR");
    this.echo("file:     " + trace[0].file, "WARNING");
    this.echo("line:     " + trace[0].line, "WARNING");
    this.echo("function: " + trace[0]["function"], "WARNING");
});

And we obtain as a result

[caption id="attachment_64" align="alignnone" width="675"]on error on error[/caption]

So, we have a generic way to obtain the errors loading resources or executing javascript of our web page. In upcoming tutorials we will explore more capabilities of CasperJs.

See you soon!

ANTLR and JavaScript

In the last few weeks I've been working on Crystal Gazer, a nodejs console application to gather information from your Git repository. Yo...… Continue reading

Calling a Step Function

Published on July 09, 2017