123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- /*
- * 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;
|