Herein I will describe how to get everything setup for a new front-end project using Yeoman, Travis, and Github Pages. When Travis successfully builds the project, the built files will be pushed into a gh-pages
branch (creating the branch if necessary). This will require use of Yeoman and the Travis CI generator for it.
Starting in an empty directory:
mkdir my-new-everything && cd my-new-everything
npm install generator-webapp
npm install generator-travis-ci
yo webapp
You could install the generators globally with the -g
flag if you like. Then do whatever you do to get it into Github. Probably, create the repository there, and then:
git init
git add .
git commit -am 'Initial commit.'
git remote add origin https://github.com/mysterycommand/my-new-everything.git
git push -u origin master
Visit your repository's settings/hooks page in Github. Something like https://github.com/username/repository/settings/hooks in this/my case this. Scroll down to the Travis hook and follow the instructions under Install Notes (ymmv, I did this a while ago and don't remember if it was difficult or not). When you've got that going (Test Hook should send you an email). Then, back in your local repo/command line:
yo travis-ci:gh-pages
… answer some prompts, and if all went well:
grunt test
Verify that the default tests pass, and then:
git add .
git commit -am 'Travis CI setup.'
Et voila … assuming your tests passed (mine did) you should have a new build in a gh-pages branch (I do) and visible to the world at something like http://username.github.io/repository (The initial publishing of your gh-pages branch can take up to 10 minutes). In this/my case: http://mysterycommand.github.io/my-new-everything works like a charm.
Next time, I'll describe my current solution for running Mocha/Chai tests within a RequireJS/AMD based project, and how to get them working both in the browser, and via the command line.
Assuming you like the setup I described here (and why wouldn't you), this will describe how to add Mocha and Chai for testing in a way that will work with RequireJS modules that live in your app/
directory.
- Split
app/scripts/main.js
file out into two files,app/scripts/main.js
andapp/scripts/config.js
. Theconfig.js
file hasmain
as a dependency, somain
,app
andbootstrap
files are loaded as before. - Setup
index.html
to point atconfig.js
, and remove theusemin
comments, like this. Note: the commented out script block pointing atscripts/app.min.js
will be useful later, so be sure to include it.
-
Install Mocha and Chai as dev dependencies:
bower install mocha --save-dev bower install chai --save-dev
-
Remove the
test/lib
directory. -
Replace the entire contents of
test/index.html
with the contents ofapp/index.html
. -
In the
head
of the newtest/index.html
, add references to the Mocha and Chai libraries installed with Bower:<link rel="stylesheet" href="../app/bower_components/mocha/mocha.css"> <script src="../app/bower_components/mocha/mocha.js"></script> <script src="../app/bower_components/chai/chai.js"></script>
-
Update the reference to the main stylesheet, to point at the generated CSS file. The
Gruntfile.js
will be updated to serve.tmp
and.
, so the relative path will be../styles/main.css
.
-
At the bottom of
test/index.html
(right before the closingbody
tag), remove the Chrome Frame conditional comment, the Google Analytics script tag, and the commented out script block referencingscripts/app.min.js
(it won't be needed for tests). -
Update the reference to RequireJS to point to
../app/bower_components/requirejs/require.js
and set it'sdata-main
attribute tospec/config
. -
Create
spec/config.js
, something like this:'use strict'; require.config({ baseUrl: '../../app/scripts/', // non-pathed dependencies should come from the app/scripts directory deps: ['runner'], // load spec/runner.js by default paths: { spec: '../../test/spec', // path tests to this directory runner: '../../test/spec/runner', // the main test runner, load all tests as dependencies appConfig: '../../app/scripts/config' // the app's config file }, shim: { runner: ['appConfig'] // make runner depend on the app's config file } });
-
Create
spec/runner.js
something like this:/* global define */ define([ // All your tests go here. 'spec/app.test' // maybe it makes sense to add tests as dependencies in spec/config? ], function () { 'use strict'; window.console = window.console || function() {}; // protect from barfs window.notrack = true; // don't track window.mocha.run(); // kickoff the runner });
-
Create
spec/app.test.js
something like this:/* global define, describe, it, should */ define(['app'], function (app) { 'use strict'; // whatever tests are in here will run as soon as this module is loaded describe('app', function () { it('should exist', function() { should.exist(app); }); }); });
Make these changes to Gruntfile.js
. The most important of these are:
-
Add
mountFolder(connect, '.'),
to theconnect:livereload
task. This adds the project root to files served atlocalhost:9000
. Tests will be available when the server is running atlocalhost:9000/test
. -
Replace
mountFolder(connect, 'test')
withmountFolder(connect, '.')
in theconnect:test
task. This is required in order fortest/index.html
to have relative path access toapp
-level dependencies. -
Update the
mocha:all
task options torun: false
andurls: ['http://localhost:<%= connect.options.port %>/test/index.html']
. Optionally set the reporter option:reporter: 'Spec'
. Run is now triggered bytest/spec/runner.js
when it (and it's dependencies) have loaded. The tests are now located intest/index.html
because we are serving the project root. -
Update the
requirejs:dist
task by adding to the options hash:include: '../bower_components/requirejs/require', mainConfigFile: yeomanConfig.app + '/scripts/config.js', out: yeomanConfig.dist + '/scripts/app.min.js'
If all went well, you should have passing tests at both http://localhost:9000/test
(while running grunt server
), and via the command line by running grunt test
. Assuming that's true, commit whatever's not committed, and push up to GitHub. When the Travis hook fires, you should also have a successful build in your gh-pages
branch!
The built files don't quite work yet. Because we're not using usemin
to rewrite our script tags, dist/index.html
has a script tag referencing bower_components/requirejs/require.js
… but no such file exists in the dist
directory.
However, that little commented out script tag referencing scripts/app.min.js
does get updated to point to the generated (RequireJS included, and rev
d) JavaScript. So how do we get dist
to use it?
Here's app/scripts/util/cleanup.js
in it's entirety (I'm not super familiar with Node, so if there's a better way to do this, please do tell). Also, please note: This script is really specific to how I have things set up. I accept no responsibility if this destroys your project and ruins your life. USE AT YOUR OWN RISK!:
'use strict';
var fs = require('fs');
var filename = process.argv[2];
// Make sure we got a filename on the command line.
if (process.argv.length < 3) {
console.log('Usage: node ' + process.argv[1] + ' FILENAME');
process.exit(1);
}
// Read the file.
fs.readFile(filename, 'utf8', function(error, data) {
var file = data;
// Find the old RequireJS script tag, and remove it.
var tag = ' <script data-main="scripts/config" src="bower_components/requirejs/require.js"></script>';
var tagIndex = file.indexOf(tag);
file = file.substring(0, tagIndex) + file.substring(tagIndex + tag.length + 1);
// Assume the last comment in the HTML file is the one you want to remove.
// TODO: Make it a comment like build:uncomment or build:remove like `usemin` does.
var beginComment = file.lastIndexOf('<!-- ');
file = file.substring(0, beginComment) + file.substring(beginComment + 5);
var endComment = file.lastIndexOf(' -->');
file = file.substring(0, endComment) + file.substring(endComment + 4);
// Write the edited file back into place.
fs.writeFile(filename, file, 'utf8', function(error) {
if (error) { throw error; }
});
});
Create this file, then add the following line to .travis.yml
between after line 45 (like here):
- node ../app/scripts/util/cleanup.js ../dist/index.html
This way, when Travis successfully builds your project, it will remove the script tag pointing at the Bower-loaded RequireJS library, and un-comment script tag pointing to the r.js
optimized, and rev
d JavaScript.
This has been a really long Pro Tip, but I hope you find it useful. If anyone knows how to improve upon cleanup.js
I'm all ears. Further, if there's a way to have Grunt run cleanup.js
on dist/index.html
during grunt:build
that'd be good (better) too.
Also, I don't really know what's going on with this formatting … for a code-specific community to not get code formatting right is a pretty huge bummer. If you have any questions you can check out the whole, un-touched after this demo project here: