blob: 24e1ccfe5ba6325385c172aee19f9d2e5a0c3b24 [file] [log] [blame]
/*
* Jake JavaScript build tool
* Copyright 2112 Matthew Eernisse (mde@fleegix.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
var path = require('path')
, fs = require('fs')
, exec = require('child_process').exec
, currDir = process.cwd();
/**
@name jake
@namespace jake
*/
/**
@name jake.TestTask
@constructor
@description Instantiating a TestTask creates a number of Jake
Tasks that make running tests for your software easy.
@param {String} name The name of the project
@param {Function} definition Defines the list of files containing the tests,
and the name of the namespace/task for running them. Will be executed on the
instantiated TestTask (i.e., 'this', will be the TestTask instance), to set
the various instance-propertiess.
@example
var t = new jake.TestTask('bij-js', function () {
this.testName = 'testSpecial';
this.testFiles.include('test/**');
});
*/
var TestTask = function (name, definition) {
var self = this;
/**
@name jake.TestTask#testNam
@public
@type {String}
@description The name of the namespace to place the tests in, and
the top-level task for running tests. Defaults to "test"
*/
this.testName = 'test';
/**
@name jake.TestTask#testFiles
@public
@type {jake.FileList}
@description The list of files containing tests to load
*/
this.testFiles = new jake.FileList();
/**
@name jake.TestTask#showDescription
@public
@type {Boolean}
@description Show the created task when doing Jake -T
*/
this.showDescription = true;
if (typeof definition == 'function') {
definition.call(this);
}
if (this.showDescription) {
desc('Run the tests for ' + name);
}
task(this.testName, function () {
var t = jake.Task[self.testName + ':run'];
t.invoke.apply(t, arguments);
}, {async: true});
namespace(self.testName, function () {
task('run', function (pat) {
var p = pat || '.*'
, re
, testFiles;
// Don't nest; make a top-level namespace. Don't want
// re-calling from inside to nest infinitely
jake.currentNamespace = jake.defaultNamespace;
re = new RegExp(pat);
testFiles = self.testFiles.toArray().filter(function (f) {
return (re).test(f);
});
// Create a namespace for all the testing tasks to live in
namespace(self.testName + 'Exec', function () {
// Each test will be a prereq for the dummy top-level task
var prereqs = []
// Continuation to pass to the async tests, wrapping `continune`
, next = function () {
complete();
}
// Create the task for this test-function
, createTask = function (name, action) {
// If the test-function is defined with a continuation
// param, flag the task as async
isAsync = !!action.length;
// Define the actual namespaced task with the name, the
// wrapped action, and the correc async-flag
task(name, createAction(name, action), {
async: isAsync
});
}
// Used as the action for the defined task for each test.
, createAction = function (n, a) {
// A wrapped function that passes in the `next` function
// for any tasks that run asynchronously
return function () {
var cb
, msg;
if (a.length) {
cb = next;
}
if (!(n == 'before' || n == 'after')) {
if (n.toLowerCase().indexOf('test') === 0) {
msg = n;
}
else {
msg = 'test ' + n;
}
jake.logger.log(n);
}
// 'this' will be the task when action is run
return a.call(this, cb);
};
}
// Dummy top-level task for everything to be prereqs for
, topLevel;
// Pull in each test-file, and iterate over any exported
// test-functions. Register each test-function as a prereq task
testFiles.forEach(function (file) {
var exp = require(path.join(currDir, file))
, name
, action
, banner
, isAsync;
// Create a namespace for each filename, so test-name collisions
// won't be a problem
namespace(file, function () {
// For displaying file banner
banner = '*** Running ' + file + ' ***';
prereqs.push(self.testName + 'Exec:' + file + ':' + banner);
// Create the task
createTask(banner, function () {});
if (typeof exp.before == 'function') {
prereqs.push(self.testName + 'Exec:' + file + ':before');
// Create the task
createTask('before', exp.before);
}
// Walk each exported function, and create a task for each
for (var p in exp) {
if (p == 'before' || p == 'after') {
continue;
}
// Add the namespace:name of this test to the list of prereqs
// for the dummy top-level task
prereqs.push(self.testName + 'Exec:' + file + ':' + p);
// Create the task
createTask(p, exp[p]);
}
if (typeof exp.after == 'function') {
prereqs.push(self.testName + 'Exec:' + file + ':after');
// Create the task
createTask('after', exp.after);
}
});
});
// Create the dummy top-level task. When calling a task internally
// with `invoke` that is async (or has async prereqs), have to listen
// for the 'complete' event to know when it's done
topLevel = task('__top__', prereqs);
topLevel.addListener('complete', function () {
jake.logger.log('All tests ran successfully');
complete();
});
topLevel.invoke(); // Do the thing!
});
}, {async: true});
});
};
jake.TestTask = TestTask;
exports.TestTask = TestTask;