forked from enviPath/enviPy
810 lines
19 KiB
JavaScript
Executable File
810 lines
19 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
var node = process.execPath
|
|
var fs = require('fs')
|
|
var spawn = require('child_process').spawn
|
|
var fg = require('foreground-child')
|
|
var opener = require('opener')
|
|
var colorSupport = require('color-support')
|
|
var nycBin = require.resolve('nyc/bin/nyc.js')
|
|
var glob = require('glob')
|
|
var isexe = require('isexe')
|
|
var osHomedir = require('os-homedir')
|
|
var yaml = require('js-yaml')
|
|
var path = require('path')
|
|
var exists = require('fs-exists-cached').sync
|
|
var os = require('os');
|
|
|
|
var coverageServiceTest = process.env.COVERAGE_SERVICE_TEST === 'true'
|
|
|
|
// NYC will not wrap a module in node_modules.
|
|
// So, we need to tell the child proc when it's been added.
|
|
if (process.env._TAP_COVERAGE_ === '1')
|
|
global.__coverage__ = global.__coverage__ || {}
|
|
|
|
// console.error(process.argv.join(' '))
|
|
// console.error('CST=%j', process.env.COVERAGE_SERVICE_TEST)
|
|
// console.error('CRT=%j', process.env.COVERALLS_REPO_TOKEN)
|
|
if (coverageServiceTest)
|
|
console.log('COVERAGE_SERVICE_TEST')
|
|
|
|
// Add new coverage services here.
|
|
// it'll check for the environ named and pipe appropriately.
|
|
//
|
|
// Currently only Coveralls is supported, but the infrastructure
|
|
// is left in place in case some noble soul fixes the codecov
|
|
// module in the future. See https://github.com/tapjs/node-tap/issues/270
|
|
var coverageServices = [
|
|
{
|
|
env: 'COVERALLS_REPO_TOKEN',
|
|
bin: require.resolve('coveralls/bin/coveralls.js'),
|
|
name: 'Coveralls'
|
|
}
|
|
]
|
|
|
|
main()
|
|
|
|
function main () {
|
|
var args = process.argv.slice(2)
|
|
|
|
if (!args.length && process.stdin.isTTY) {
|
|
console.error(usage())
|
|
process.exit(1)
|
|
}
|
|
|
|
// set default args
|
|
var defaults = constructDefaultArgs()
|
|
|
|
// parse dotfile
|
|
var rcFile = process.env.TAP_RCFILE || (osHomedir() + '/.taprc')
|
|
var rcOptions = parseRcFile(rcFile)
|
|
|
|
// supplement defaults with parsed rc options
|
|
if (rcOptions)
|
|
Object.keys(rcOptions).forEach(function (k) {
|
|
defaults[k] = rcOptions[k]
|
|
})
|
|
|
|
defaults.rcFile = rcFile
|
|
|
|
// parse args
|
|
var options = parseArgs(args, defaults)
|
|
|
|
if (!options)
|
|
return
|
|
|
|
process.stdout.on('error', function (er) {
|
|
if (er.code === 'EPIPE')
|
|
process.exit()
|
|
else
|
|
throw er
|
|
})
|
|
|
|
options.files = globFiles(options.files)
|
|
|
|
if ((options.coverageReport || options.checkCoverage) &&
|
|
options.files.length === 0)
|
|
return runCoverageReport(options)
|
|
|
|
if (options.files.length === 0) {
|
|
console.error('Reading TAP data from stdin (use "-" argument to suppress)')
|
|
options.files.push('-')
|
|
}
|
|
|
|
if (options.files.length === 1 && options.files[0] === '-') {
|
|
if (options.coverage)
|
|
console.error('Coverage disabled because stdin cannot be instrumented')
|
|
stdinOnly(options)
|
|
return
|
|
}
|
|
|
|
// By definition, the next block cannot be covered, because
|
|
// they are only relevant when coverage is turned off.
|
|
|
|
/* istanbul ignore if */
|
|
if (options.coverage && !global.__coverage__)
|
|
return respawnWithCoverage(options)
|
|
|
|
setupTapEnv(options)
|
|
|
|
runTests(options)
|
|
}
|
|
|
|
function constructDefaultArgs () {
|
|
var defaultTimeout = 30
|
|
if (global.__coverage__)
|
|
defaultTimeout = 240
|
|
|
|
var defaultArgs = {
|
|
nodeArgs: [],
|
|
nycArgs: [],
|
|
testArgs: [],
|
|
timeout: +process.env.TAP_TIMEOUT || defaultTimeout,
|
|
color: !!colorSupport.level,
|
|
reporter: null,
|
|
files: [],
|
|
grep: [],
|
|
grepInvert: false,
|
|
bail: false,
|
|
saveFile: null,
|
|
pipeToService: false,
|
|
coverageReport: null,
|
|
browser: true,
|
|
coverage: undefined,
|
|
checkCoverage: false,
|
|
branches: 0,
|
|
functions: 0,
|
|
lines: 0,
|
|
statements: 0,
|
|
jobs: 1,
|
|
outputFile: null
|
|
}
|
|
|
|
if (process.env.TAP_COLORS !== undefined)
|
|
defaultArgs.color = !!(+process.env.TAP_COLORS)
|
|
|
|
return defaultArgs
|
|
}
|
|
|
|
function parseArgs (args, defaults) {
|
|
var options = defaults || {}
|
|
|
|
var singleFlags = {
|
|
b: 'bail',
|
|
B: 'no-bail',
|
|
i: 'invert',
|
|
I: 'no-invert',
|
|
c: 'color',
|
|
C: 'no-color',
|
|
T: 'no-timeout',
|
|
J: 'jobs-auto',
|
|
O: 'only',
|
|
h: 'help',
|
|
'?': 'help',
|
|
v: 'version'
|
|
}
|
|
|
|
var singleOpts = {
|
|
j: 'jobs',
|
|
g: 'grep',
|
|
R: 'reporter',
|
|
t: 'timeout',
|
|
s: 'save',
|
|
o: 'output-file'
|
|
}
|
|
|
|
// If we're running under Travis-CI with a Coveralls.io token,
|
|
// then it's a safe bet that we ought to output coverage.
|
|
for (var i = 0; i < coverageServices.length && !options.pipeToService; i++) {
|
|
if (process.env[coverageServices[i].env])
|
|
options.pipeToService = true
|
|
}
|
|
|
|
var defaultCoverage = options.pipeToService
|
|
var dumpConfig = false
|
|
|
|
for (i = 0; i < args.length; i++) {
|
|
var arg = args[i]
|
|
if (arg.charAt(0) !== '-' || arg === '-') {
|
|
options.files.push(arg)
|
|
continue
|
|
}
|
|
|
|
// short-flags
|
|
if (arg.charAt(1) !== '-' && arg !== '-gc') {
|
|
var expand = []
|
|
for (var f = 1; f < arg.length; f++) {
|
|
var fc = arg.charAt(f)
|
|
var sf = singleFlags[fc]
|
|
var so = singleOpts[fc]
|
|
if (sf)
|
|
expand.push('--' + sf)
|
|
else if (so) {
|
|
var soval = arg.slice(f + 1)
|
|
if (soval.charAt(0) !== '=') {
|
|
soval = '=' + soval
|
|
}
|
|
expand.push('--' + so + soval)
|
|
f = arg.length
|
|
} else if (arg !== '-' + fc)
|
|
expand.push('-' + fc)
|
|
}
|
|
if (expand.length) {
|
|
args.splice.apply(args, [i, 1].concat(expand))
|
|
i--
|
|
continue
|
|
}
|
|
}
|
|
|
|
var key = arg
|
|
var val = null
|
|
if (key.match(/^--/) && arg.indexOf('=') !== -1) {
|
|
var kv = arg.split('=')
|
|
key = kv.shift()
|
|
val = kv.join('=')
|
|
}
|
|
|
|
switch (key) {
|
|
case '--help':
|
|
console.log(usage())
|
|
return null
|
|
|
|
case '--dump-config':
|
|
dumpConfig = true
|
|
continue
|
|
|
|
case '--nyc-help':
|
|
nycHelp()
|
|
return null
|
|
|
|
case '--nyc-version':
|
|
nycVersion()
|
|
return null
|
|
|
|
case '--version':
|
|
console.log(require('../package.json').version)
|
|
return null
|
|
|
|
case '--jobs':
|
|
val = val || args[++i]
|
|
options.jobs = +val
|
|
continue
|
|
|
|
case '--jobs-auto':
|
|
val = os.cpus().length;
|
|
options.jobs = +val
|
|
continue
|
|
|
|
case '--coverage-report':
|
|
options.coverageReport = val || args[++i]
|
|
if (options.coverageReport === 'html')
|
|
options.coverageReport = 'lcov'
|
|
defaultCoverage = true
|
|
continue
|
|
|
|
case '--no-browser':
|
|
options.browser = false
|
|
continue
|
|
|
|
case '--no-coverage-report':
|
|
options.coverageReport = false
|
|
continue
|
|
|
|
case '--no-cov': case '--no-coverage':
|
|
options.coverage = false
|
|
continue
|
|
|
|
case '--cov': case '--coverage':
|
|
options.coverage = true
|
|
continue
|
|
|
|
case '--save':
|
|
val = val || args[++i]
|
|
options.saveFile = val
|
|
continue
|
|
|
|
case '--reporter':
|
|
val = val || args[++i]
|
|
options.reporter = val
|
|
continue
|
|
|
|
case '--gc': case '-gc': case '--expose-gc':
|
|
options.nodeArgs.push('--expose-gc')
|
|
continue
|
|
|
|
case '--strict':
|
|
options.nodeArgs.push('--use_strict')
|
|
continue
|
|
|
|
case '--debug':
|
|
options.nodeArgs.push('--debug')
|
|
continue
|
|
|
|
case '--debug-brk':
|
|
options.nodeArgs.push('--debug-brk')
|
|
continue
|
|
|
|
case '--harmony':
|
|
options.nodeArgs.push('--harmony')
|
|
continue
|
|
|
|
case '--node-arg':
|
|
val = val || args[++i]
|
|
if (val !== undefined)
|
|
options.nodeArgs.push(val)
|
|
continue
|
|
|
|
case '--check-coverage':
|
|
defaultCoverage = true
|
|
options.checkCoverage = true
|
|
continue
|
|
|
|
case '--test-arg':
|
|
val = val || args[++i]
|
|
if (val !== undefined)
|
|
options.testArgs.push(val)
|
|
continue
|
|
|
|
case '--nyc-arg':
|
|
val = val || args[++i]
|
|
if (val !== undefined)
|
|
options.nycArgs.push(val)
|
|
continue
|
|
|
|
case '--100':
|
|
defaultCoverage = true
|
|
options.checkCoverage = true
|
|
options.branches = 100
|
|
options.functions = 100
|
|
options.lines = 100
|
|
options.statements = 100
|
|
continue
|
|
|
|
case '--branches':
|
|
case '--functions':
|
|
case '--lines':
|
|
case '--statements':
|
|
defaultCoverage = true
|
|
val = val || args[++i]
|
|
options.checkCoverage = true
|
|
options[key.slice(2)] = val
|
|
continue
|
|
|
|
case '--color':
|
|
options.color = true
|
|
continue
|
|
|
|
case '--no-color':
|
|
options.color = false
|
|
continue
|
|
|
|
case '--output-file':
|
|
val = val || args[++i]
|
|
if (val !== undefined)
|
|
options.outputFile = val
|
|
continue
|
|
|
|
case '--no-timeout':
|
|
options.timeout = 0
|
|
continue
|
|
|
|
case '--timeout':
|
|
val = val || args[++i]
|
|
options.timeout = +val
|
|
continue
|
|
|
|
case '--invert':
|
|
options.grepInvert = true
|
|
continue
|
|
|
|
case '--no-invert':
|
|
options.grepInvert = false
|
|
continue
|
|
|
|
case '--grep':
|
|
val = val || args[++i]
|
|
if (val !== undefined)
|
|
options.grep.push(strToRegExp(val))
|
|
continue
|
|
|
|
case '--bail':
|
|
options.bail = true
|
|
continue
|
|
|
|
case '--no-bail':
|
|
options.bail = false
|
|
continue
|
|
|
|
case '--only':
|
|
options.only = true
|
|
continue
|
|
|
|
case '--':
|
|
options.files = options.files.concat(args.slice(i + 1))
|
|
i = args.length
|
|
continue
|
|
|
|
default:
|
|
throw new Error('Unknown argument: ' + arg)
|
|
}
|
|
}
|
|
|
|
if (options.coverage === undefined)
|
|
options.coverage = defaultCoverage
|
|
|
|
if (process.env.TAP === '1')
|
|
options.reporter = 'tap'
|
|
|
|
// default to tap for non-tty envs
|
|
if (!options.reporter)
|
|
options.reporter = options.color ? 'classic' : 'tap'
|
|
|
|
if (dumpConfig)
|
|
return console.log(JSON.stringify(options, null, 2))
|
|
|
|
return options
|
|
}
|
|
|
|
/* istanbul ignore next */
|
|
function respawnWithCoverage (options) {
|
|
// console.error('respawn with coverage')
|
|
// Re-spawn with coverage
|
|
var args = [nycBin].concat(
|
|
'--silent',
|
|
'--cache=true',
|
|
options.nycArgs,
|
|
'--',
|
|
process.execArgv,
|
|
process.argv.slice(1)
|
|
)
|
|
process.env._TAP_COVERAGE_ = '1'
|
|
var child = fg(node, args)
|
|
child.removeAllListeners('close')
|
|
child.on('close', function (code, signal) {
|
|
runCoverageReport(options, code, signal)
|
|
})
|
|
}
|
|
|
|
function pipeToCoverageServices (options, child) {
|
|
// console.error('pipe to services')
|
|
var piped = false
|
|
for (var i = 0; i < coverageServices.length; i++) {
|
|
// console.error('pipe to service?', coverageServices[i].env)
|
|
if (process.env[coverageServices[i].env]) {
|
|
pipeToCoverageService(coverageServices[i], options, child)
|
|
piped = true
|
|
}
|
|
}
|
|
|
|
/* istanbul ignore if */
|
|
if (!piped)
|
|
throw new Error('unknown service, internal error')
|
|
}
|
|
|
|
function pipeToCoverageService (service, options, child) {
|
|
// console.error('pipe to service yes', service.env)
|
|
var bin = service.bin
|
|
|
|
if (coverageServiceTest) {
|
|
// console.error('use fakey test bin')
|
|
// test scaffolding.
|
|
// don't actually send stuff to the service
|
|
bin = require.resolve('../test/fixtures/cat.js')
|
|
console.log('%s:%s', service.name, process.env[service.env])
|
|
}
|
|
|
|
var ca = spawn(node, [bin], {
|
|
stdio: [ 'pipe', 1, 2 ]
|
|
})
|
|
|
|
child.stdout.pipe(ca.stdin)
|
|
|
|
ca.on('close', function (code, signal) {
|
|
if (signal)
|
|
process.kill(process.pid, signal)
|
|
else if (code)
|
|
console.log('Error piping coverage to ' + service.name)
|
|
else
|
|
console.log('Successfully piped to ' + service.name)
|
|
})
|
|
}
|
|
|
|
function runCoverageReport (options, code, signal) {
|
|
if (options.checkCoverage)
|
|
runCoverageCheck(options, code, signal)
|
|
else
|
|
runCoverageReportOnly(options, code, signal)
|
|
}
|
|
|
|
function runCoverageReportOnly (options, code, signal) {
|
|
if (options.coverageReport === false)
|
|
return close(code, signal)
|
|
|
|
if (!options.coverageReport) {
|
|
if (options.pipeToService || coverageServiceTest)
|
|
options.coverageReport = 'text-lcov'
|
|
else
|
|
options.coverageReport = 'text'
|
|
}
|
|
|
|
var args = [nycBin, 'report', '--reporter', options.coverageReport]
|
|
// console.error('run coverage report', args)
|
|
|
|
var child
|
|
// automatically hook into coveralls
|
|
if (options.coverageReport === 'text-lcov' && options.pipeToService) {
|
|
// console.error('pipeToService')
|
|
child = spawn(node, args, { stdio: [ 0, 'pipe', 2 ] })
|
|
pipeToCoverageServices(options, child)
|
|
} else {
|
|
// otherwise just run the reporter
|
|
child = fg(node, args)
|
|
if (options.coverageReport === 'lcov' && options.browser)
|
|
child.on('exit', function () {
|
|
opener('coverage/lcov-report/index.html')
|
|
})
|
|
}
|
|
|
|
if (code || signal) {
|
|
child.removeAllListeners('close')
|
|
child.on('close', close)
|
|
}
|
|
|
|
function close (c, s) {
|
|
if (signal || s) {
|
|
setTimeout(function () {}, 200)
|
|
return process.kill(process.pid, signal || s)
|
|
}
|
|
if (code || c)
|
|
return process.exit(code || c)
|
|
}
|
|
}
|
|
|
|
function coverageCheckArgs (options) {
|
|
var args = []
|
|
if (options.branches)
|
|
args.push('--branches', options.branches)
|
|
if (options.functions)
|
|
args.push('--functions', options.functions)
|
|
if (options.lines)
|
|
args.push('--lines', options.lines)
|
|
if (options.statements)
|
|
args.push('--statements', options.statements)
|
|
|
|
return args
|
|
}
|
|
|
|
function runCoverageCheck (options, code, signal) {
|
|
var args = [nycBin, 'check-coverage'].concat(coverageCheckArgs(options))
|
|
|
|
var child = fg(node, args)
|
|
child.removeAllListeners('close')
|
|
child.on('close', function (c, s) {
|
|
runCoverageReportOnly(options, code || c, signal || s)
|
|
})
|
|
}
|
|
|
|
function usage () {
|
|
return fs.readFileSync(__dirname + '/usage.txt', 'utf8')
|
|
.split('@@REPORTERS@@')
|
|
.join(getReporters())
|
|
}
|
|
|
|
function nycHelp () {
|
|
fg(node, [nycBin, '--help'])
|
|
}
|
|
|
|
function nycVersion () {
|
|
console.log(require('nyc/package.json').version)
|
|
}
|
|
|
|
function getReporters () {
|
|
var types = require('tap-mocha-reporter').types
|
|
types = types.reduce(function (str, t) {
|
|
var ll = str.split('\n').pop().length + t.length
|
|
if (ll < 40)
|
|
return str + ' ' + t
|
|
else
|
|
return str + '\n' + t
|
|
}, '').trim()
|
|
var ind = ' '
|
|
return ind + types.split('\n').join('\n' + ind)
|
|
}
|
|
|
|
function setupTapEnv (options) {
|
|
process.env.TAP_TIMEOUT = options.timeout
|
|
if (options.color)
|
|
process.env.TAP_COLORS = 1
|
|
else
|
|
process.env.TAP_COLORS = 0
|
|
|
|
if (options.bail)
|
|
process.env.TAP_BAIL = '1'
|
|
|
|
if (options.grepInvert)
|
|
process.env.TAP_GREP_INVERT = '1'
|
|
|
|
if (options.grep.length)
|
|
process.env.TAP_GREP = options.grep.map(function (pattern) {
|
|
return pattern.toString()
|
|
}).join('\n')
|
|
|
|
if (options.only)
|
|
process.env.TAP_ONLY = '1'
|
|
}
|
|
|
|
function globFiles (files) {
|
|
return files.reduce(function (acc, f) {
|
|
if (f === '-') {
|
|
acc.push(f)
|
|
return acc
|
|
}
|
|
|
|
// glob claims patterns MUST not include any '\'s
|
|
if (!/\\/.test(f))
|
|
f = glob.sync(f) || f
|
|
|
|
return acc.concat(f)
|
|
}, [])
|
|
}
|
|
|
|
function makeReporter (options) {
|
|
var TMR = require('tap-mocha-reporter')
|
|
return new TMR(options.reporter)
|
|
}
|
|
|
|
function stdinOnly (options) {
|
|
// if we didn't specify any files, then just passthrough
|
|
// to the reporter, so we don't get '/dev/stdin' in the suite list.
|
|
// We have to pause() before piping to switch streams2 into old-mode
|
|
process.stdin.pause()
|
|
var reporter = makeReporter(options)
|
|
process.stdin.pipe(reporter)
|
|
process.stdin.resume()
|
|
}
|
|
|
|
function readSaveFile (options) {
|
|
if (options.saveFile)
|
|
try {
|
|
return fs.readFileSync(options.saveFile, 'utf8').trim().split('\n')
|
|
} catch (er) {}
|
|
|
|
return null
|
|
}
|
|
|
|
function saveFails (options, tap) {
|
|
if (!options.saveFile)
|
|
return
|
|
|
|
var fails = []
|
|
var successes = []
|
|
tap.on('result', function (res) {
|
|
// we will continue to re-run todo tests, even though they're
|
|
// not technically "failures".
|
|
if (!res.ok && !res.extra.skip)
|
|
fails.push(res.extra.file)
|
|
else
|
|
successes.push(res.extra.file)
|
|
})
|
|
|
|
tap.on('bailout', function (reason) {
|
|
// add any pending test files to the fails list.
|
|
fails.push.apply(fails, options.files.filter(function (file) {
|
|
return successes.indexOf(file) === -1
|
|
}))
|
|
save()
|
|
})
|
|
|
|
tap.on('end', save)
|
|
|
|
function save () {
|
|
fails = fails.reduce(function (set, f) {
|
|
if (set.indexOf(f) === -1)
|
|
set.push(f)
|
|
return set
|
|
}, [])
|
|
|
|
if (!fails.length)
|
|
try {
|
|
fs.unlinkSync(options.saveFile)
|
|
} catch (er) {}
|
|
else
|
|
try {
|
|
fs.writeFileSync(options.saveFile, fails.join('\n') + '\n')
|
|
} catch (er) {}
|
|
}
|
|
}
|
|
|
|
function filterFiles (files, saved, parallelOk) {
|
|
return files.filter(function (file) {
|
|
if (path.basename(file) === 'tap-parallel-ok')
|
|
parallelOk[path.resolve(path.dirname(file))] = true
|
|
else if (path.basename(file) === 'tap-parallel-not-ok')
|
|
parallelOk[path.resolve(path.dirname(file))] = false
|
|
else
|
|
return saved === null || saved.indexOf(file) !== -1
|
|
})
|
|
}
|
|
|
|
function isParallelOk (parallelOk, file) {
|
|
var dir = path.resolve(path.dirname(file))
|
|
|
|
if (dir in parallelOk)
|
|
return parallelOk[dir]
|
|
|
|
if (exists(dir + '/tap-parallel-ok'))
|
|
return parallelOk[dir] = true
|
|
|
|
if (exists(dir + '/tap-parallel-not-ok'))
|
|
return parallelOk[dir] = false
|
|
|
|
if (dir.length >= process.cwd().length)
|
|
return isParallelOk(parallelOk, dir)
|
|
}
|
|
|
|
function runAllFiles (options, saved, tap) {
|
|
var doStdin = false
|
|
var parallelOk = Object.create(null)
|
|
|
|
options.files = filterFiles(options.files, saved, parallelOk)
|
|
|
|
for (var i = 0; i < options.files.length; i++) {
|
|
var opt = {}
|
|
var file = options.files[i]
|
|
|
|
// Pick up stdin after all the other files are handled.
|
|
if (file === '-') {
|
|
doStdin = true
|
|
continue
|
|
}
|
|
|
|
var st = fs.statSync(options.files[i])
|
|
if (options.timeout)
|
|
opt.timeout = options.timeout * 1000
|
|
|
|
opt.file = file
|
|
if (options.jobs > 1)
|
|
opt.buffered = isParallelOk(parallelOk, file) !== false
|
|
|
|
if (file.match(/\.js$/)) {
|
|
var args = options.nodeArgs.concat(file).concat(options.testArgs)
|
|
tap.spawn(node, args, opt, file)
|
|
} else if (st.isDirectory()) {
|
|
var dir = filterFiles(fs.readdirSync(file).map(function (f) {
|
|
return file + '/' + f
|
|
}), saved, parallelOk)
|
|
options.files.push.apply(options.files, dir)
|
|
} else if (isexe.sync(options.files[i]))
|
|
tap.spawn(options.files[i], options.testArgs, opt, file)
|
|
}
|
|
|
|
if (doStdin)
|
|
tap.stdin()
|
|
}
|
|
|
|
function runTests (options) {
|
|
var saved = readSaveFile(options)
|
|
|
|
// At this point, we know we need to use the tap root,
|
|
// because there are 1 or more files to spawn.
|
|
var tap = require('../lib/tap.js')
|
|
tap.runOnly = false
|
|
|
|
// greps are passed to children, but not the runner itself
|
|
tap.grep = []
|
|
tap.jobs = options.jobs
|
|
tap.patchProcess()
|
|
|
|
// if not -Rtap, then output what the user wants.
|
|
// otherwise just dump to stdout
|
|
tap.pipe(options.reporter === 'tap' ? process.stdout: makeReporter(options))
|
|
|
|
// need to replay the first version line, because the previous
|
|
// line will have flushed it out to stdout or the reporter already.
|
|
if (options.outputFile !== null)
|
|
tap.pipe(fs.createWriteStream(options.outputFile)).write('TAP version 13\n')
|
|
|
|
saveFails(options, tap)
|
|
|
|
runAllFiles(options, saved, tap)
|
|
|
|
tap.end()
|
|
}
|
|
|
|
function parseRcFile (path) {
|
|
try {
|
|
var contents = fs.readFileSync(path, 'utf8')
|
|
return yaml.safeLoad(contents) || {}
|
|
} catch (er) {
|
|
// if no dotfile exists, or invalid yaml, fail gracefully
|
|
return {}
|
|
}
|
|
}
|
|
|
|
function strToRegExp (g) {
|
|
var p = g.match(/^\/(.*)\/([a-z]*)$/)
|
|
g = p ? p[1] : g
|
|
var flags = p ? p[2] : ''
|
|
return new RegExp(g, flags)
|
|
}
|