You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
8.3 KiB
270 lines
8.3 KiB
/* |
|
* 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. |
|
* |
|
*/ |
|
|
|
let path = require('path'); |
|
let 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 |
|
let t = new jake.TestTask('bij-js', function () { |
|
this.testName = 'testSpecial'; |
|
this.testFiles.include('test/**'); |
|
}); |
|
|
|
*/ |
|
let TestTask = function () { |
|
let self = this; |
|
let args = Array.prototype.slice.call(arguments); |
|
let name = args.shift(); |
|
let definition = args.pop(); |
|
let prereqs = args.pop() || []; |
|
|
|
/** |
|
@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; |
|
|
|
/* |
|
@name jake.TestTask#totalTests |
|
@public |
|
@type {Number} |
|
@description The total number of tests to run |
|
*/ |
|
this.totalTests = 0; |
|
|
|
/* |
|
@name jake.TestTask#executedTests |
|
@public |
|
@type {Number} |
|
@description The number of tests successfully run |
|
*/ |
|
this.executedTests = 0; |
|
|
|
if (typeof definition == 'function') { |
|
definition.call(this); |
|
} |
|
|
|
if (this.showDescription) { |
|
desc('Run the tests for ' + name); |
|
} |
|
|
|
task(this.testName, prereqs, {async: true}, function () { |
|
let t = jake.Task[this.fullName + ':run']; |
|
t.on('complete', function () { |
|
complete(); |
|
}); |
|
// Pass args to the namespaced test |
|
t.invoke.apply(t, arguments); |
|
}); |
|
|
|
namespace(self.testName, function () { |
|
|
|
let runTask = task('run', {async: true}, function (pat) { |
|
let re; |
|
let 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); |
|
// Get test files that match the passed-in pattern |
|
testFiles = self.testFiles.toArray() |
|
.filter(function (f) { |
|
return (re).test(f); |
|
}) // Don't load the same file multiple times -- should this be in FileList? |
|
.reduce(function (p, c) { |
|
if (p.indexOf(c) < 0) { |
|
p.push(c); |
|
} |
|
return p; |
|
}, []); |
|
|
|
// 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 |
|
let prereqs = []; |
|
// Continuation to pass to the async tests, wrapping `continune` |
|
let next = function () { |
|
complete(); |
|
}; |
|
// Create the task for this test-function |
|
let createTask = function (name, action) { |
|
// If the test-function is defined with a continuation |
|
// param, flag the task as async |
|
let t; |
|
let isAsync = !!action.length; |
|
|
|
// Define the actual namespaced task with the name, the |
|
// wrapped action, and the correc async-flag |
|
t = task(name, createAction(name, action), { |
|
async: isAsync |
|
}); |
|
t.once('complete', function () { |
|
self.executedTests++; |
|
}); |
|
t._internal = true; |
|
return t; |
|
}; |
|
// Used as the action for the defined task for each test. |
|
let createAction = function (n, a) { |
|
// A wrapped function that passes in the `next` function |
|
// for any tasks that run asynchronously |
|
return function () { |
|
let cb; |
|
if (a.length) { |
|
cb = next; |
|
} |
|
if (!(n == 'before' || n == 'after' || |
|
/_beforeEach$/.test(n) || /_afterEach$/.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 |
|
let 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) { |
|
let exp = require(path.join(currDir, file)); |
|
|
|
// Create a namespace for each filename, so test-name collisions |
|
// won't be a problem |
|
namespace(file, function () { |
|
let testPrefix = self.testName + 'Exec:' + file + ':'; |
|
let testName; |
|
// Dummy task for displaying file banner |
|
testName = '*** Running ' + file + ' ***'; |
|
prereqs.push(testPrefix + testName); |
|
createTask(testName, function () {}); |
|
|
|
// 'before' setup |
|
if (typeof exp.before == 'function') { |
|
prereqs.push(testPrefix + 'before'); |
|
// Create the task |
|
createTask('before', exp.before); |
|
} |
|
|
|
// Walk each exported function, and create a task for each |
|
for (let p in exp) { |
|
if (p == 'before' || p == 'after' || |
|
p == 'beforeEach' || p == 'afterEach') { |
|
continue; |
|
} |
|
|
|
if (typeof exp.beforeEach == 'function') { |
|
prereqs.push(testPrefix + p + '_beforeEach'); |
|
// Create the task |
|
createTask(p + '_beforeEach', exp.beforeEach); |
|
} |
|
|
|
// Add the namespace:name of this test to the list of prereqs |
|
// for the dummy top-level task |
|
prereqs.push(testPrefix + p); |
|
// Create the task |
|
createTask(p, exp[p]); |
|
|
|
if (typeof exp.afterEach == 'function') { |
|
prereqs.push(testPrefix + p + '_afterEach'); |
|
// Create the task |
|
createTask(p + '_afterEach', exp.afterEach); |
|
} |
|
} |
|
|
|
// 'after' teardown |
|
if (typeof exp.after == 'function') { |
|
prereqs.push(testPrefix + 'after'); |
|
// Create the task |
|
let afterTask = createTask('after', exp.after); |
|
afterTask._internal = true; |
|
} |
|
|
|
}); |
|
}); |
|
|
|
self.totalTests = prereqs.length; |
|
process.on('exit', function () { |
|
// Throw in the case where the process exits without |
|
// finishing tests, but no error was thrown |
|
if (!jake.errorCode && (self.totalTests > self.executedTests)) { |
|
throw new Error('Process exited without all tests completing.'); |
|
} |
|
}); |
|
|
|
// 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._internal = true; |
|
topLevel.addListener('complete', function () { |
|
jake.logger.log('All tests ran successfully'); |
|
complete(); |
|
}); |
|
|
|
topLevel.invoke(); // Do the thing! |
|
}); |
|
|
|
}); |
|
runTask._internal = true; |
|
|
|
}); |
|
|
|
|
|
}; |
|
|
|
jake.TestTask = TestTask; |
|
exports.TestTask = TestTask; |
|
|
|
|