Skip to content

Instantly share code, notes, and snippets.

@n1k0
Last active December 12, 2015 08:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save n1k0/baba2fe72a1363fc11f8 to your computer and use it in GitHub Desktop.
Save n1k0/baba2fe72a1363fc11f8 to your computer and use it in GitHub Desktop.
Get some performance metrics about your site using CasperJS

Quick Performance Audit

This CasperJS script will record all requests made by a given page url and compute some metrics about them.

The script

// performance.js
var casper = require("casper").create()
  , utils  = require("utils")
  , url    = casper.cli.get(0)
  , start  = new Date().getTime()
  , times  = []
  , sortBy = casper.cli.get('sort') === 'size' ? 'size' : 'time';

if (!url) {
    casper.echo("The url arg is missing").exit();
}

function formatNumber(number, chars, unit) {
    if (!number) {
        return new Array(chars + 1 + unit.length).join(' ');
    }
    var len = number.toString().length
      , newLen = chars - len + 1;
    return new Array(newLen).join(' ') + number.toString() + unit;
}

function formatSize(bytes) {
    var sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
    if (bytes === 0) return 'N/A';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10);
    return (Math.round(bytes / Math.pow(1024, i) * 100) / 100) + sizes[i];
}

function formatTime(ms) {
    var seconds = ms / 1000;
    if (seconds < 1) return ms + 'ms';
    if (seconds < 60) return (Math.round(seconds * 100) / 100) + 's';
    return ms + 'ms';
}

function formatEntry(req, maxUrlChars) {
    var truncatedUrl = req.url
      , charsDiff = maxUrlChars - req.url.length;
    if (charsDiff < 0) {
        var pad = Math.floor(maxUrlChars / 2);
        truncatedUrl = req.url.slice(0, pad - 1) + '…' + req.url.slice(req.url.length - pad, req.url.length);
    } else {
        truncatedUrl += new Array(charsDiff + 1).join('…');
    }
    return utils.format("HTTP %s %s %s %s",
                        req.status || "???",
                        truncatedUrl,
                        formatNumber(req.size, 7, 'B'),
                        formatNumber(req.time, 7, 'ms'));
}

casper.on('resource.requested', function(resource) {
    times[resource.id] = {
        start: new Date().getTime(),
        url: resource.url,
        size: resource.size,
    };
});

casper.on('resource.received', function(resource) {
    if (resource.stage !== "end") return;
    times[resource.id].time = new Date().getTime() - times[resource.id].start;
    times[resource.id].status = resource.status;
    try {
        times[resource.id].size = parseInt(resource.headers.filter(function(header) {
            return header.name.toLowerCase().trim() === "content-length";
        })[0].value, 10);
    } catch (e) {
        times[resource.id].size = 0;
    }
});

casper.start(url).run(function() {
    times = times.filter(function(entry) {
        return entry !== null && entry.size;
    });
    var totalTime = new Date().getTime() - start
      , totalSize = times.map(function(entry) {
        return entry.size || 0;
    }).reduce(function(a, b) {
        return a + b;
    });
    this.echo(utils.format('Total loaded: %s %s', formatSize(totalSize), formatTime(totalTime)));
    times.sort(function(reqa, reqb) {
        if (sortBy === 'time') {
            return reqb.time - reqa.time;
        }
        return reqb.size - reqa.size;
    }).forEach(function(req) {
        this.echo(formatEntry(req, 50));
    }.bind(this));
    this.exit();
});

Usage

$ casperjs performance.js https://nicolas.perriault.net/
Total loaded: 184.3KB 2.81s
HTTP 200 https://nicolas.perriaul…sunset-shooters/thumb.jpg   17820B    1743ms
HTTP 200 https://nicolas.perriaul…mmunism-is-dead/thumb.jpg   44240B    1692ms
HTTP 200 https://nicolas.perriaul…2012/espiguette/thumb.jpg   17821B    1256ms
HTTP 200 https://nicolas.perriaul…le-de-maguelone/thumb.jpg   13009B    1241ms
HTTP 200 https://nicolas.perriault.net/static/img/me.png………   42779B    1216ms
HTTP 200 https://nicolas.perriaul…w-the-black-dog/thumb.jpg   22451B    1135ms
HTTP 200 https://nicolas.perriaul…dali-s-cadillac/thumb.jpg   15678B    1049ms
HTTP 200 https://nicolas.perriaul…12/mystery-door/thumb.jpg    8429B    1041ms
HTTP 200 https://nicolas.perriaul…y/2012/cadaques/thumb.jpg    6239B     819ms
HTTP 200 http://fonts.googleapis.…?family=Asap&subset=latin     255B     165ms

The --size option allows to sort the results by size:

$ casperjs performance.js https://nicolas.perriault.net/ --sort=size
Total loaded: 184.3KB 4.15s
HTTP 200 https://nicolas.perriaul…mmunism-is-dead/thumb.jpg   44240B    3139ms
HTTP 200 https://nicolas.perriault.net/static/img/me.png………   42779B    1319ms
HTTP 200 https://nicolas.perriaul…w-the-black-dog/thumb.jpg   22451B    1065ms
HTTP 200 https://nicolas.perriaul…2012/espiguette/thumb.jpg   17821B    1016ms
HTTP 200 https://nicolas.perriaul…sunset-shooters/thumb.jpg   17820B    1541ms
HTTP 200 https://nicolas.perriaul…dali-s-cadillac/thumb.jpg   15678B     991ms
HTTP 200 https://nicolas.perriaul…le-de-maguelone/thumb.jpg   13009B    1258ms
HTTP 200 https://nicolas.perriaul…12/mystery-door/thumb.jpg    8429B    1195ms
HTTP 200 https://nicolas.perriaul…y/2012/cadaques/thumb.jpg    6239B    1518ms
HTTP 200 http://fonts.googleapis.…?family=Asap&subset=latin     255B     171ms
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment