Testing Static Website Creation with Grunt and Mocha

/

I gave a talk recently for the Southern Maine Web Developer Group on the topic of the Grunt task runner. The slideshow for the talk was itself the example project of Grunt in action.

The slideshow is built (purposely) with many Grunt plugins as a static website. This site may then be uploaded to Amazon S3 to host the live version. Because testing is vital I wanted to add something to the Grunt workflow that used tests. For a static website there was not much around the way of testing to do, besides html validation (which became it's own can-of-worms) so I thought validating the s3 upload would be a good place to use a test.

There were a few technologies in this mix besides Grunt: Jade templating and another TJ Holowaychuk project, Mocha for the tests.

The plan was this:

  • Generate something unique for the build
  • Insert into a comment on the index page
  • After s3 upload run the test
  • Look for the unique comment on the live site

The key to this approach was Grunt's own templating and one specific plugin to Grunt to make it all come together, grunt-env.

You can see the entire Gruntfile.js, trimmed here to the pertinent bits:

// ...
grunt.initConfig({
// ...
    generatedTime : (new Date()).toISOString(), 
// ...  
  env : {
    vars : {
      DATETIME : '<%= generatedTime %>'
    }
  },
// ...  
  jade: {
    options: {
      pretty: false,
      data : {
    datetime : '<%= generatedTime %>',
    version : '<%= pkg.version %>'
      }
    },
    templates: {expand: true , cwd: 'src/jade', src: ['*.jade'], dest: 'tmp/', ext: '.html'}
  },
// ...
  test: {
// ...
    remote : {
      src: 'test/remote.js'
    }
  }
});
// ...

I picked the datetime of the grunt generation as the reasonably unique value and generated it into the 'generatedTime' external(/internal?) data variable thing. Which could then be injected into the index file using the Grunt's templating and Jade's external data import feature:

//
  Generated @ #{datetime}, version #{version}

At this point the template generates into a HTML file with the datetime of the generation embedded into the comment (and version via a similar mechanism). Now we let Grunt process normally and upload to S3, at which point in this target we want to run the test.

The major problem here being there was no way to template the 'generatedTime' into our test. In comes grunt-env a simple plugin which allows for a more standard means for communication between plugins, by storing data in the ENV. In the Grunt configuration we pass generatedTime into the local environment using grunt-env:

env : {
  vars : {
    DATETIME : '<%= generatedTime %>'
  }
}

Now our test file will have access to the generated time, at which point it is straightforward-ish to run a request against the live server:

var indexURL = 'http://grunt.thomasleen.com/index.html';
var datetime = process.env.DATETIME;

describe('dist/index.html', function(){  
  it('should be this generated version', function(done){
    request(indexURL, function (error, response, body) {
      (error === null).should.be.true;
      body.should.be.a.String;
      body.should.containEql('Generated @ ' + datetime);
      body.should.containEql('version ' + pkg.version);
      return done();
    });
  });
});

datetime is coming via the generatedTime variable passed to the environment with grunt-env. The same value which should have been passed to the jade template which was then generated and uploaded to the live website.