Created
February 12, 2013 05:47
-
-
Save stoyan/4760524 to your computer and use it in GitHub Desktop.
AsciiDoc unit test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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