How often did you want to have a great end-to-end testing framework for your Meteor app, but couldn’t find one? There are several solutions available, such as Laika and Velocity, but they don’t really provide instruments good enough to test your app from the user’s perspective: click here, check if this label is displayed, then click there, then check this value, etc.

The enlightenment came when I realized that Meteor app is nothing more than just another app that runs inside the browser. In case of E2E tests, when you don’t care about the app’s internals, it doesn’t really matter what tool you would use. It just should be flexible, fast and convenient, because you’re going to spend up to 1/4 of your development time with it!

Out of many available solutions I chose CasperJS. It’s easy to use, mature, and it works seamlessly with Grunt. “Why Grunt?” you might ask. Grunt in my opinion is the best runner for JS apps, be it pure Node.js, AngularJS, or Meteor.

To keep things nice and clean, I wrap Meteor projects in Grunt like that:

.
├── Gruntfile.js
├── LICENSE
├── README.md
├── bundle
├── meteor
│   ├── client
│   ├── packages
│   ├── packages.json
│   ├── public
│   └── server
├── node_modules
│   ├── grunt
│   ├── grunt-autoprefixer
│   ├── grunt-casper
├── package.json
└── tests
    ├── e2e.js
    └── fixtures

As you can see, my Meteor app sits comfortably in its own directory. For the brevity’s sake I’m showing the tree just one level deep, but you can read more about efficiently structuring your Meteor app here.

This is the example of how a typical CasperJS end-to-end testing looks like:

casper.test.begin('Running E2E Tests', 1, function(test){

  casper.start(url, function() {
    casper.viewport(1280, 720).then(function() {
      this.test.comment('Make sure page header is there');
      casper.waitForSelector('h1.text-center', function() {
        this.test.assertTextExists('HTML2Jade Converter', 'Header exists');
      });
    });
  });

  casper.then(function() {
    this.test.comment('Make sure CodeMirror attached to the textareas');
    casper.waitForSelector('#div-html .CodeMirror-scroll', function() {
      this.test.comment('CodeMirror is attached to HTML textarea');
    });
    casper.waitForSelector('#div-jade .CodeMirror-scroll', function() {
      this.test.comment('CodeMirror is attached to Jade textarea');
    });
  });
});

Pretty simple, eh? The basic idea is to wait until a specific DOM element is rendered, and then do the stuff. You can find more great CasperJS examples here and here.

CasperJS can work with both PhantomJS and SlimerJS. I prefer the latter because it’s more of a “real” browser, and because it’s faster. You might need to review your tests before switching from one to another, as I’ve noticed some inconsistencies in how these two render DOM and wait for events.

You can find the completed app on GitHub. It’s also available online, so please take a look, especially if you’re using Jade in your Meteor projects.