-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
2f05aca
commit 766cfee
Showing
9 changed files
with
316 additions
and
201 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
'use strict'; | ||
var EventEmitter = require('events').EventEmitter; | ||
var path = require('path'); | ||
var util = require('util'); | ||
var fs = require('fs'); | ||
var flatten = require('arr-flatten'); | ||
var Promise = require('bluebird'); | ||
var figures = require('figures'); | ||
var assign = require('object-assign'); | ||
var globby = require('globby'); | ||
var chalk = require('chalk'); | ||
var fork = require('./lib/fork'); | ||
|
||
function Api(files, options) { | ||
if (!(this instanceof Api)) { | ||
return new Api(files, options); | ||
} | ||
|
||
EventEmitter.call(this); | ||
|
||
assign(this, options); | ||
|
||
this.rejectionCount = 0; | ||
this.exceptionCount = 0; | ||
this.passCount = 0; | ||
this.failCount = 0; | ||
this.fileCount = 0; | ||
this.testCount = 0; | ||
this.errors = []; | ||
this.stats = []; | ||
this.tests = []; | ||
this.files = files || []; | ||
|
||
Object.keys(Api.prototype).forEach(function (key) { | ||
this[key] = this[key].bind(this); | ||
}, this); | ||
} | ||
|
||
util.inherits(Api, EventEmitter); | ||
module.exports = Api; | ||
|
||
Api.prototype._runFile = function (file) { | ||
var args = [file]; | ||
|
||
if (this.failFast) { | ||
args.push('--fail-fast'); | ||
} | ||
|
||
if (this.serial) { | ||
args.push('--serial'); | ||
} | ||
|
||
// Forward the `time-require` `--sorted` flag. | ||
// Intended for internal optimization tests only. | ||
if (this._sorted) { | ||
args.push('--sorted'); | ||
} | ||
|
||
return fork(args) | ||
.on('stats', this._handleStats) | ||
.on('test', this._handleTest) | ||
.on('unhandledRejections', this._handleRejections) | ||
.on('uncaughtException', this._handleExceptions); | ||
}; | ||
|
||
Api.prototype._handleRejections = function (data) { | ||
this.rejectionCount += data.rejections.length; | ||
|
||
data.rejections.forEach(function (err) { | ||
err.type = 'rejection'; | ||
err.file = data.file; | ||
this.emit('error', err); | ||
this.errors.push(err); | ||
}, this); | ||
}; | ||
|
||
Api.prototype._handleExceptions = function (err) { | ||
this.exceptionCount++; | ||
err.type = 'exception'; | ||
this.emit('error', err); | ||
this.errors.push(err); | ||
}; | ||
|
||
Api.prototype._handleStats = function (stats) { | ||
this.testCount += stats.testCount; | ||
}; | ||
|
||
Api.prototype._handleTest = function (test) { | ||
test.title = this._prefixTitle(test.file) + test.title; | ||
|
||
var isError = test.error.message; | ||
|
||
if (isError) { | ||
this.errors.push(test); | ||
} else { | ||
test.error = null; | ||
} | ||
|
||
this.emit('test', test); | ||
}; | ||
|
||
Api.prototype._prefixTitle = function (file) { | ||
if (this.fileCount === 1) { | ||
return ''; | ||
} | ||
|
||
var separator = ' ' + chalk.gray.dim(figures.pointerSmall) + ' '; | ||
|
||
var base = path.dirname(this.files[0]); | ||
|
||
if (base === '.') { | ||
base = this.files[0] || 'test'; | ||
} | ||
|
||
base += path.sep; | ||
|
||
var prefix = path.relative('.', file) | ||
.replace(base, '') | ||
.replace(/\.spec/, '') | ||
.replace(/test\-/g, '') | ||
.replace(/\.js$/, '') | ||
.split(path.sep) | ||
.join(separator); | ||
|
||
if (prefix.length > 0) { | ||
prefix += separator; | ||
} | ||
|
||
return prefix; | ||
}; | ||
|
||
Api.prototype.run = function () { | ||
var self = this; | ||
|
||
return handlePaths(this.files) | ||
.map(function (file) { | ||
return path.resolve(file); | ||
}) | ||
.then(function (files) { | ||
if (files.length === 0) { | ||
return Promise.reject(new Error('Couldn\'t find any files to test')); | ||
} | ||
|
||
self.fileCount = files.length; | ||
|
||
var tests = files.map(self._runFile); | ||
|
||
// receive test count from all files and then run the tests | ||
var statsCount = 0; | ||
var deferred = Promise.pending(); | ||
|
||
tests.forEach(function (test) { | ||
var counted = false; | ||
|
||
function tryRun() { | ||
if (counted) { | ||
return; | ||
} | ||
|
||
if (++statsCount === self.fileCount) { | ||
self.emit('ready'); | ||
|
||
var method = self.serial ? 'mapSeries' : 'map'; | ||
|
||
deferred.resolve(Promise[method](files, function (file, index) { | ||
return tests[index].run(); | ||
})); | ||
} | ||
} | ||
|
||
test.on('stats', tryRun); | ||
test.catch(tryRun); | ||
}); | ||
|
||
return deferred.promise; | ||
}) | ||
.then(function (results) { | ||
// assemble stats from all tests | ||
self.stats = results.map(function (result) { | ||
return result.stats; | ||
}); | ||
|
||
self.tests = results.map(function (result) { | ||
return result.tests; | ||
}); | ||
|
||
self.tests = flatten(self.tests); | ||
|
||
self.passCount = sum(self.stats, 'passCount'); | ||
self.failCount = sum(self.stats, 'failCount'); | ||
}); | ||
}; | ||
|
||
function handlePaths(files) { | ||
if (files.length === 0) { | ||
files = [ | ||
'test.js', | ||
'test-*.js', | ||
'test/*.js' | ||
]; | ||
} | ||
|
||
files.push('!**/node_modules/**'); | ||
|
||
// convert pinkie-promise to Bluebird promise | ||
files = Promise.resolve(globby(files)); | ||
|
||
return files | ||
.map(function (file) { | ||
if (fs.statSync(file).isDirectory()) { | ||
return handlePaths([path.join(file, '*.js')]); | ||
} | ||
|
||
return file; | ||
}) | ||
.then(flatten) | ||
.filter(function (file) { | ||
return path.extname(file) === '.js' && path.basename(file)[0] !== '_'; | ||
}); | ||
} | ||
|
||
function sum(arr, key) { | ||
var result = 0; | ||
|
||
arr.forEach(function (item) { | ||
result += item[key]; | ||
}); | ||
|
||
return result; | ||
} |
Oops, something went wrong.