Skip to content

Instantly share code, notes, and snippets.

@stoyan
Created February 12, 2013 05:47
Show Gist options
  • Save stoyan/4760524 to your computer and use it in GitHub Desktop.
Save stoyan/4760524 to your computer and use it in GitHub Desktop.
AsciiDoc unit test
// dependencies where I can see them
var assert = require('assert');
var fs = require('fs');
var jslint = require('jshint').JSHINT;
// buncha vars
var snip, rawsnip, start = false, skipping = false, collecting = false;
var passed = 0, skipped = 0;
var lints = 0, nolints = 0;
// short
var log = console.log;
// JSHint options need to be more relaxed because the book also
// points out bad patterns
var lintopts = {
indent: 2, // 2 spaces for indentation
trailing: true, // disallow spaces at the end of a line
white: true,
plusplus: false, // allows ++ and --
browser: true, // assumes some common browser globals exist
// such as `document`
node: true, // assumes the code can run in node.js
// and globals such as `global` are defined
expr: true, // ok to have expressions that seemingly do nothing
// such as `a; // true` which the samples use to show
// result values
loopfunc: true, // allows definition of a function in a loop
// for educational purposes (in the part about closures)
newcap: false, // allows calling constructors (capitalized functions)
// without `new`, again just for educational purposes
proto: true, // allows using `__proto__` which is great for understanding
// prototypes, although it's not supported in all browsers
};
// read the book one line at a time
fs.readFileSync('book.asc').toString().split('\n').forEach(function(src, num) {
num++;
line = src.trim();
// collect JS snippets
if (line === '[source,js]') {
collecting = start = true;
return;
}
if (start) {
start = false;
if (line.indexOf('////') > 0) {
skipping = true;
return;
}
if (line.indexOf('--//--') === -1) { // new snippet
// --//-- signals this snippet depends on the previous
// used for longer snippets that are broken up by explanation
snip = line.indexOf('++--') === -1 ? '"use strict";\n' : ''; // ++ says "dont use strict"
rawsnip = snip; // we want to lint both raw and instrumented snippets
// so far they are the same
}
return;
}
if (collecting || skipping) {
if (line.indexOf('---------') === 0) { // end of snippet
if (skipping) {
collecting = skipping = false;
skipped++;
return;
}
if (snip.indexOf('/*nolint*/') === -1) {
// lint both instrumented and original snippets
lint(snip);
lint(rawsnip);
lints++;
} else {
nolints++;
}
// run the snippet
exec(snip);
collecting = false;
} else if (!skipping){ // yet another line, instrument and add to the snippet
snip += prep(src, num) + '\n';
rawsnip += src + '\n';
}
}
});
// Add asserts
function prep(l, n) {
var parts = l.split(/;\s*\/\/::/); // "//::" separates expression to execute from its result
var nonspace = parts[0].match(/\S/);
var spaces = nonspace === null ? "" : Array(nonspace.index + 1).join(" ");
parts[0] = parts[0].trim();
if (parts[1]) {
var r = parts[1].split(/\s*(,,|::)\s*/)[0].trim(); // the result may have ,, or ::, ignore what's on the right
// e.g. //:: true,, of course!
// e.g. //:: ReferenceError::Invalid whatever
if (r.indexOf('Error') !== -1) {
// expect //:: Error to throw
return spaces + 'assert.throws(function () {' + parts[0] + '; }, ' + r + ', "error line #' + n + '");';
}
if (r === 'NaN') {
// special NaN case
return spaces + 'assert(isNaN(' + parts[0] + '), true, "error line #' + n + '");'
}
// usual
return spaces + 'assert.deepEqual(' + parts[0] + ', ' + r + ', "error line #' + n + '");';
}
return l;
}
// run a snippet
function exec(snip) {
// muck some stuff up and zap log()
var mock = "function define(){}; function alert(){}; console.log = function(){};";
try {
eval(snip + mock);
passed++;
} catch (e) {
log('------');
log(snip);
log('------');
log(e.message);
process.exit();
}
}
// lint a snippet
function lint(snip) {
// lint the snippet with all the options and have it assume
// assert objext exists
if (!jslint(snip, lintopts, {assert: false})) {
log('------');
log(snip);
log('------');
log(jslint.errors[0]);
process.exit();
}
}
// report
log("passed: " + passed + ', skipped: ' + skipped);
log("linted: " + lints + ', nolints: ' + nolints);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment