Skip to content

Commit

Permalink
[minor] initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
3rd-Eden committed Jul 8, 2014
0 parents commit 1970369
Show file tree
Hide file tree
Showing 6 changed files with 411 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
8 changes: 8 additions & 0 deletions bin/illuminati
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env node

'use strict';

var Illuminati = require('../')
, illuminati = new Illuminati(process.cwd());

illuminati.run();
46 changes: 46 additions & 0 deletions browserify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';

var convert = require('convert-source-map')
, browserify = require('browserify')
, path = require('path')
, fs = require('fs');

/**
* Compile all the things.
*
* @param {Array} files Files to be included in the bundle.
* @param {Object} options Options for browserify.
* @param {Function} fn Completion callback.
* @api public
*/
module.exports = function compile(files, options, fn) {
if ('function' === typeof options) {
fn = options;
options = {};
}

var b = browserify(options);

//
// Introduce our `assume` library by default in to the package so you only
// need Illuminati to start testing your applications and add all other files
// that are needed to test all the things.
//
b.require('assume');
files.forEach(b.add.bind(b));

b.bundle({
debug: true // Ensure that browserify is compiled with source-maps.
}, function bundled(err, source) {
if (err) return fn(err);

//
// PhantomJS does not understand base64 encoded source maps so we have to
// convert the created sourcemap to a JSON file which we can serve from our
// server.
//
var map = convert.fromSource(source);

fn(undefined, convert.removeComments(source), map ? map.toObject() : undefined);
});
};
20 changes: 20 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="/mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="/mocha.js"></script>
<script>
mocha.ui('{illuminati:ui}');
mocha.reporter('{illuminati:reporter}');
</script>
<script src="/illuminati.js"></script>
<script>
if (window.mochaPhantomJS) mochaPhantomJS.run();
else mocha.run();
</script>
</body>
</html>
297 changes: 297 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
'use strict';

var browserify = require('./browserify')
, minimatch = require("minimatch")
, child = require('child_process')
, dot = require('dot-component')
, fusing = require('fusing')
, path = require('path')
, fs = require('fs');

/**
* Illuminati: Secret society of testers.
*
* @constructor
* @param {String} dir Directory we're loaded in.
* @param {Object} options Optional options.
* @api public
*/
function Illuminati(dir, options) {
if (!(this instanceof Illuminati)) return new Illuminati(dir, options);
var self = this;

this.fuse();
this.root = dir; // Root directory

if (fs.existsSync(path.join(dir, 'package.json'))) {
(function merge(data) {
Object.keys(data || {}).forEach(function each(key) {
self.conf[key] = +data[key] || data[key];
});
})(require(path.join(dir, 'package.json')).illuminati);
}
}

fusing(Illuminati, require('eventemitter3'));

/**
* Find the configuration for the Illuminati runner. We assume that the
* configuration is added in a special `illuminati` key in the users
* `package.json` which can be used to configure various of things in the
* project. This configuration will be merged with our default illuminati
* configuration from our own `package.json` file.
*
* @type {Object}
* @public
*/
Illuminati.writable('conf', require('./package.json').illuminati);

/**
* Command line arguments.
*
* @type {Object}
* @public
*/
Illuminati.readable('argv', require('argh').argv);

/**
* The glob pattern we use for searching valid test files.
*
* @type {String}
* @public
*/
Illuminati.readable('glob', '*.test.js');

/**
* The assets that need to be served.
*
* @type {Array}
* @public
*/
Illuminati.writable('assets', [
'./node_modules/mocha/mocha.js',
'./node_modules/mocha/mocha.css',
'./index.html'
]);

/**
* Run the actual test suites.
*
* @api public
*/
Illuminati.readable('run', function run() {
var illuminati = this;

/**
* The tests have run.
*
* @param {Error} err
* @api private
*/
function ran(err) {
if (err) return process.exit(1);
return process.exit(0);
}

if (!this.argv.phantom) return this.mocha(ran);

this.server(function listening(err) {
illuminati.phantomjs(ran);
});
});

/**
* Create our HTTP server and start listening on the provide port number.
*
* @param {Function} fn Completion callback, server is listening
* @api private
*/
Illuminati.readable('server', function server(fn) {
var app = require('http').createServer(this.incoming.bind(this))
, illuminati = this;

this.assets = this.assets.map(this.map);

browserify(this.files, this.conf.browserify || {
basedir: this.root
}, function (err, source, map) {
source += '//# sourceMappingURL=/illuminati.json';
map.file = '/illuminati.js';

illuminati.assets.push({
data: source,
url: '/illuminati.js',
type: 'text/javascript'
}, {
data: map,
url: '/illuminati.json',
type: 'application/json'
});

require('connected')(app, illuminati.conf.port, fn);
});

//
// We don't want our HTTP server to hold up the destruction of the world. So
// we unref it.
//
if (app.unref) app.unref();

return this;
});

/**
* Run the tests on PhantomJS.
*
* @param {Number} port The port number our server is listening on.
* @param {Function} fn Completion callback.
* @api private
*/
Illuminati.readable('phantomjs', function phantomjs(fn) {
var phantom = child.spawn(
path.join(__dirname, 'node_modules', '.bin', 'mocha-phantomjs'), [
'http://localhost:'+ this.conf.port
], {
stdio: 'inherit'
});

phantom.on('close', function exit(code) {
if (code) {
return fn(new Error('Tests failed to run, returned exit code: '+ code));
}

fn();
});

return this;
});

/**
* Run the tests against the regular mocha.
*
* @param {Function} fn Completion function.
* @api private
*/
Illuminati.readable('mocha', function mochas(fn) {
var mocha = child.spawn(
path.join(__dirname, 'node_modules', '.bin', 'mocha'), [
'--reporter', this.conf.reporter,
'--ui', this.conf.ui
].concat(this.files), {
stdio: 'inherit'
});

mocha.on('close', function exit(code) {
if (code) {
return fn(new Error('Tests failed to run, returned exit code: '+ code));
}

fn();
});

return this;
});

/**
* Find the files that we need to test for.
*
* @type {Array}
* @public
*/
Illuminati.get('files', function files() {
if (this.argv.argv) return this.argv.argv;

var illuminati = this;

return [
path.join(illuminati.root, 'tests'),
path.join(illuminati.root, 'test')
].reduce(function reduce(files, dir) {
var folder;

try { folder = fs.readdirSync(dir); }
catch (e) { return files; }

Array.prototype.push.apply(files, folder.filter(function filter(file) {
return minimatch(file, illuminati.glob);
}).map(function map(file) {
return path.join(dir, file);
}));

return files;
}, []);
});

/**
* Introduce template tags into the given template.
*
* @param {Object} data Information to merge.
* @param {String} template Template to replace
* @returns {String} The template
* @api private
*/
Illuminati.readable('introduce', function introduce(data, template) {
var key; template = template.toString();

while (key = /{illuminati:([^{]+?)}/gm.exec(template)) {
key = key[0];
template = template.replace(key, dot.get(data, key.slice(12, -1)));
}

return template;
});

/**
* Map assets to an object that we can serve.
*
* @param {String} file Filename/address.
* @returns {Object} Serve-able object
* @api public
*/
Illuminati.readable('map', function map(file) {
if ('string' !== typeof file) {
return file; // Already processed, do not give a fuck
}

return {
data: fs.readFileSync(path.join(__dirname, file), 'utf-8'),
url: '/'+ path.basename(file),
type: {
js: 'text/javascript',
css: 'text/css',
html: 'text/html',
json: 'application/json'
}[path.extname(file).slice(1)] || 'text/plain'
};
});

/**
* Answer HTTP requests to our incoming test server.
*
* @param {Request} req HTTP request.
* @param {Response} res HTTP response.
* @api private
*/
Illuminati.readable('incoming', function incoming(req, res) {
res.statusCode = 200;

var illuminati = this;

if (req.url === '/') req.url = '/index.html';
if (illuminati.assets.some(function some(asset) {
if (asset.url !== req.url) return false;

res.setHeader('Content-Type', asset.type);
res.end(illuminati.introduce(illuminati.conf, asset.data));

return true;
})) return;

res.statusCode = 404;
res.end('404: Please read the documentation on: '+ illuminati.conf.homepage);
});

//
// Expose the module.
//
module.exports = Illuminati;
Loading

0 comments on commit 1970369

Please sign in to comment.