ickyrr-gagarin
Version:
A current updated fork of gagarin
256 lines (207 loc) • 6.68 kB
JavaScript
/**
* Module dependencies.
*/
var tools = require('../tools');
var Mocha = require('mocha');
var Base = require('mocha').reporters.Base;
var chalk = require('chalk');
var path = require('path');
var util = require('util');
var url = require('url');
var logs2 = require('../logs2');
var table = require('../logs2/table');
var logs = require('../logs');
var Build = require('../meteor/build');
var Promise = require('es6-promise').Promise;
var createParallelReporterFactory = require('./reporters').createParallelReporterFactory;
module.exports = Gagarin;
/**
* Creates Gagarin with `options`.
*
* It inherits everything from Mocha except that the ui
* is always set to "gagarin".
*
* @param {Object} options
*/
function Gagarin (options) {
"use strict";
var write = process.stdout.write.bind(process.stdout);
var velocity = null;
var listOfFrameworks = [];
var numberOfLinesPrinted = 0;
// XXX gagarin user interface is defined here
require('./interface');
options.settings = tools.getSettings(options.settings);
options.ui = 'gagarin';
if (options.velocity) {
velocity = require('../velocity/reporter')(function () {
var parsed = url.parse(options.velocity);
return Promise.resolve({ host: parsed.hostname, port: parsed.port || 443 });
});
}
var factory = !options.parallel ? null : createParallelReporterFactory(function (allStats, elapsed) {
if (numberOfLinesPrinted > 0) {
write('\u001b[' + numberOfLinesPrinted + 'A')
}
write(' elapsed time: ' + Math.floor(elapsed / 100) / 10 + 's' + '\n\n');
numberOfLinesPrinted = 2 + table(allStats, write);
});
function getMocha() {
if (options.parallel === 0 && listOfFrameworks.length > 0) {
return listOfFrameworks[0];
}
var mocha = new Mocha(options);
if (factory) {
// overwrite the default reporter
mocha._reporter = factory(listOfFrameworks.length);
}
var reporter = mocha._reporter;
if (velocity) {
// hijack the active reporter
mocha._reporter = function (runner, options) {
// install the velocity reporter listeners ...
velocity(runner);
// ... but return the original value
return new reporter(runner, options);
}
}
listOfFrameworks.push(mocha);
return mocha;
}; // getMocha
this.options = options;
this.addFile = function (file) {
getMocha().addFile(file);
}
this.runAllFrameworks = function (callback) {
var listOfErrors = [];
var pending = listOfFrameworks.slice(0);
var counter = 0;
var running = 0;
if (factory) {
// looks like parallel test runner, so make sure
// there are no logs which can break the table view
factory.reset();
process.stdout.write('\n\n');
write = logs2.block();
logs.setSilentBuild(true);
}
listOfFrameworks.forEach(function (mocha) {
mocha.loadFiles();
mocha.files = [];
});
function finish() {
if (factory) {
logs2.unblock();
factory.epilogue();
}
callback && callback(listOfErrors, counter);
}
function maybeFinish (action) {
if (running <= 0 && pending <= 0) {
finish();
} else {
action && action();
}
}
function update() {
var availableSlots = options.parallel > 0 ? options.parallel : 1;
if (running >= availableSlots) {
return false;
}
var next = pending.shift();
if (!next) {
maybeFinish();
return false;
}
running += 1;
try {
next.run(function (numberOfFailures) {
counter += numberOfFailures;
running -= 1;
maybeFinish(function () { // if not ...
while (update()); // run as many suites as you can
});
});
} catch (err) {
listOfErrors.push(err);
running -= 1;
maybeFinish();
return false;
}
return true;
}
while (update()); // run as many suites as you can
}
}
/**
* A not-so-thin wrapper around Mocha.run; first build the
* meteor app, then run the tests.
*
* @param {Function} callback
*/
Gagarin.prototype.run = function (callback) {
"use strict";
var pathToApp = this.options.pathToApp || path.resolve('.');
var skipBuild = !!this.options.skipBuild;
var buildOnly = !!this.options.buildOnly;
var muteBuild = !!this.options.muteBuild;
var verbose = buildOnly || (this.options.verbose !== undefined ? !!this.options.verbose : false);
var buildTimeout = this.options.buildTimeout;
var self = this;
process.stdout.write('\n');
var title = (skipBuild ? 'skipped ' : '') + 'building app => ' + pathToApp;
var counter = 0;
var spinner = '/-\\|';
var handle = muteBuild && setInterval(function () {
var animated = chalk.yellow(spinner.charAt(counter++ % spinner.length));
process.stdout.write(
chalk.yellow(' -') + animated + chalk.yellow('- ') + title + chalk.yellow(' -') + animated + chalk.yellow('-\r')
);
}, 100);
if (!muteBuild) {
process.stdout.write(chalk.green(' --- ') + chalk.gray(title) + chalk.green(' ---\n\n'));
}
new Build({
pathToApp : pathToApp,
skipBuild : skipBuild,
verbose : !muteBuild,
timeout : buildTimeout
}).start()
.then(function () {
if (muteBuild) {
clearInterval(handle);
process.stdout.write(chalk.green(' --- ') + chalk.gray(title) + chalk.green(' ---\r'));
}
if (!muteBuild) {
process.stdout.write(chalk.green('\n done building ...\n\n'));
}
if (buildOnly) {
callback(0);
} else {
self.runAllFrameworks(function (listOfErrors, failures) {
if (listOfErrors.length > 0) {
// since we're now loading all files prior to an further actions, this should no longer happen
// but I am leaving this error report for now in case something unexpected happens
console.error(chalk.red(' The following errors occured while configuring some test suites:\n'));
}
listOfErrors.forEach(function (err) {
console.error(err.stack.split('\n').map(function (line) {
return ' ' + line;
}).join('\n') + '\n');
});
callback(failures + listOfErrors.length);
});
}
}, function (err) {
// clear the loading spinner
process.stdout.write(new Array(title.length + 12).join(' '));
clearInterval(handle);
throw err;
})
.catch(function (err) {
// make sure the error passes through promise
setTimeout(function () {
throw err;
});
});
};