Using CasperJS to Automate Server Migration Testing

casperjs-logo-dark

CasperJs is an open source navigation scripting & testing utility written in Javascript by Nicolas Perriault for the PhantomJS WebKit headless browser. It also works with Gecko-based SlimerJS as an alternative engine.

What does all of that mean? It means you can emulate navigation steps just as you would in a browser — without the browser.

I’ve played with PhantomJS in the past out of curiosity, but never thought it would become an important tool in my sysadmin toolbox. Well, it is now.

I was recently tasked with migrating sugarCRM instances. There are enough of them that manually testing each login post-migration was not something I was looking forward to. It’s fairly trivial to do this with cURL, but I wanted to try taking this a step further. I wanted to take a screenshot of the page after logging in, and save it to a file — all automagically. Enter CasperJS.

First, we create a new Casper instance. Setting verbose and debug logLevel is very useful for testing, but it’s optional. Notice there is a built-in test framework as well!

phantom.casperTest = true;
require("utils");

var casper = require('casper').create({
	verbose: true, 
	logLevel: 'debug',
	pageSettings: {
		userAgent: 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20140611 Firefox/24.0 Iceweasel/24.6.0'
	}
});

Next, since I’m a sysadmin and linux junkie, I live in the command-line. So I’m using the CLI options parsing features built right in to CasperJS. I think the below is pretty self-explanatory and the options parsing just works:

Sample CLI usage:

kevin@kevops:~/$ casperjs ./sugarcrm-login.js --host="some-sugarcrm-site.com" --user="admin" --pass="p4ssw0rd" --ssl --imgdir="/home/kevin/screenshots/"

var host 		= casper.cli.get('host');
var user_name 		= casper.cli.get('user');
var user_password 	= casper.cli.get('pass');
var scheme		= 'http://';
var imgdir		= '/tmp/';

if(casper.cli.has('ssl')) { var scheme = 'https://'; }
if(casper.cli.has('imgdir')) { var imgdir = casper.cli.get('imgdir'); }

var base_uri = scheme + host;

Add some some event listeners. The first one allows us to locally print console.log messages. The second one emits when a page leaves a Javascript error uncaught.
casper.on('remote.message', function(msg) {
    this.echo('remote message caught: ' + msg);
});

casper.on("page.error", function(msg, trace) {
    this.echo("Page Error: " + msg, "ERROR");
});

Time to fire it up. One of the great things that casperJS adds to something like phantomJS is the ability to write simple code in a procedural way. This is important when you’re testing page navigation for a website, and avoids some of the headache from javascript’s asynchronous nature. The first thing we do is call casper.start() . This is what first loads the url.

Note that I’m trying out the test framework here, just to see how it works. It’s simple and intuitive if you’re familiar with testing frameworks.

casper.start(base_uri, function() {
	this.test.assertExists('form#form', 'form found!');
});

We determined that the form exists with our test statement, so now we need to fill in the fields and submit. Keep in mind, .fill() is looking for the “name” property of the form fields, which is user_name nand user_password in the case of sugarCRM logins.
casper.then(function() {
	this.fill('form#form', {
		'user_name':		user_name,
		'user_password':	user_password,
	}, true);
});

How does it work?
magic
So that filled out the form, submitted it, and went to the next step, just like you would using a browser. Is it really that simple? Yup.

Even more magical, we can take a screenshot once we login, and save it. How cool is that?

// login and grab snapshot
casper.then(function() {
	casper.viewport(1024, 768);
	this.capture(imgdir + host + '_login.jpg', undefined, {
        	quality: 100
	});
});

casper.run();

casper.run() is the final call, that kicks off the whole thing.

So now, since these sugarCRM sites all share a superadmin login and password, I can do something like this post migration:

#!/bin/bash
grep -i servername /etc/httpd/conf/httpd.conf | awk '{print $2}' | \
while IFS= read -r domain; do
  casperjs ./sugarcrm-login.js --host="$domain" --admin="admin" --pass="p4ssw0rd" --imgdir="/home/kevin/tmp/sugar-migrations/screenshots/"
done

Of course, I think I’ll add a lot more testing, and more commandline options to test if both http and https are working, and probably click through the admin panel for more thorough post-migration testing. I think I’d also like to create separate log files for each domain, but I’m not sure yet.

My next casperJS project will be to automate my daily work clock-ins. We have to login and logout of a web portal each day at work. Automating this each day with a screenshot is a perfect use case, and I’ll have meticulous records in case there’s ever an attendance discrepancy. 😎

sugarcrm-login.js:

phantom.casperTest = true;
require("utils");

var casper = require('casper').create({
	verbose: true, 
	logLevel: 'debug',
	pageSettings: {
		userAgent: 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20140611 Firefox/24.0 Iceweasel/24.6.0'
	}
});

var host 		= casper.cli.get('host');
var user_name 		= casper.cli.get('user');
var user_password 	= casper.cli.get('pass');
var scheme		= 'http://';
var imgdir		= '/tmp/';

if(casper.cli.has('ssl')) { var scheme = 'https://'; }
if(casper.cli.has('imgdir')) { var imgdir = casper.cli.get('imgdir'); }

var base_uri = scheme + host;

casper.on('remote.message', function(msg) {
    this.echo('remote message caught: ' + msg);
});

casper.on("page.error", function(msg, trace) {
    this.echo("Page Error: " + msg, "ERROR");
});

casper.start(base_uri, function() {
	this.test.assertExists('form#form', 'form found!');
});

casper.then(function() {
	this.fill('form#form', {
		'user_name':		user_name,
		'user_password':	user_password,
	}, true);
});

// login and grab snapshot
casper.then(function() {
	casper.viewport(1024, 768);
	this.capture(imgdir + host + '_login.jpg', undefined, {
        	quality: 100
	});
});

casper.run();

Leave a Reply

Your email address will not be published. Required fields are marked *