diff --git a/api.js b/api.js index 2b20af905..e4e490571 100644 --- a/api.js +++ b/api.js @@ -12,8 +12,6 @@ var debounce = require('lodash.debounce'); var ms = require('ms'); var AvaError = require('./lib/ava-error'); var fork = require('./lib/fork'); -var CachingPrecompiler = require('./lib/caching-precompiler'); -var AvaFiles = require('./lib/ava-files'); var RunStatus = require('./lib/run-status'); function Api(options) { @@ -43,13 +41,17 @@ util.inherits(Api, EventEmitter); module.exports = Api; Api.prototype._runFile = function (file, runStatus) { - var hash = this.precompiler.precompileFile(file); - var precompiled = {}; - precompiled[file] = hash; + var options = objectAssign({}, this.options); - var options = objectAssign({}, this.options, { - precompiled: precompiled - }); + var babelEnabled = this.options.babelEnabled !== false; + + if (babelEnabled) { + var hash = this.precompiler.precompileFile(file); + var precompiled = {}; + precompiled[file] = hash; + + options.precompiled = precompiled; + } var emitter = fork(file, options); @@ -70,17 +72,7 @@ Api.prototype._onTimeout = function (runStatus) { runStatus.emit('timeout'); }; -Api.prototype.run = function (files, options) { - var self = this; - - return new AvaFiles(files) - .findTestFiles() - .then(function (files) { - return self._run(files, options); - }); -}; - -Api.prototype._run = function (files, _options) { +Api.prototype.run = function (files, _options) { var self = this; var runStatus = new RunStatus({ prefixTitles: this.options.explicitTitles || files.length > 1, @@ -108,12 +100,19 @@ Api.prototype._run = function (files, _options) { return Promise.resolve(runStatus); } - var cacheEnabled = self.options.cacheEnabled !== false; - var cacheDir = (cacheEnabled && findCacheDir({name: 'ava', files: files})) || - uniqueTempDir(); + var babelEnabled = self.options.babelEnabled !== false; + + if (babelEnabled) { + var CachingPrecompiler = require('./lib/caching-precompiler'); + + var cacheEnabled = self.options.cacheEnabled !== false; + var cacheDir = (cacheEnabled && findCacheDir({name: 'ava', files: files})) || + uniqueTempDir(); + + self.options.cacheDir = cacheDir; + self.precompiler = new CachingPrecompiler(cacheDir, self.options.babelConfig); + } - self.options.cacheDir = cacheDir; - self.precompiler = new CachingPrecompiler(cacheDir, self.options.babelConfig); self.fileCount = files.length; var overwatch; diff --git a/browser-index.js b/browser-index.js new file mode 100644 index 000000000..01ee98a32 --- /dev/null +++ b/browser-index.js @@ -0,0 +1,91 @@ +'use strict'; +var path = require('path'); +var chalk = require('chalk'); +var serializeError = require('./lib/serialize-error'); +var globals = require('./lib/globals'); +var Runner = require('./lib/runner'); +var send = require('./lib/send'); + +var opts = globals.options; +var runner = new Runner({ + serial: opts.serial, + bail: opts.failFast, + match: opts.match +}); + +// note that test files have require('ava') +require('./lib/test-worker').avaRequired = true; + +// if fail-fast is enabled, use this variable to detect +// that no more tests should be logged +var isFailed = false; + +Error.stackTraceLimit = Infinity; + +function test(props) { + if (isFailed) { + return; + } + + var hasError = typeof props.error !== 'undefined'; + + // don't display anything if it's a passed hook + if (!hasError && props.type !== 'test') { + return; + } + + if (hasError) { + props.error = serializeError(props.error); + } else { + props.error = null; + } + + send('test', props); + + if (hasError && opts.failFast) { + isFailed = true; + exit(); + } +} + +function exit() { + var stats = runner._buildStats(); + + send('results', { + stats: stats + }); + + globals.setTimeout(close, 100); +} + +globals.setImmediate(function () { + var hasExclusive = runner.tests.hasExclusive; + var numberOfTests = runner.tests.tests.concurrent.length + runner.tests.tests.serial.length; + + if (numberOfTests === 0) { + send('no-tests', {avaRequired: true}); + return; + } + + send('stats', { + testCount: numberOfTests, + hasExclusive: hasExclusive + }); + + runner.on('test', test); + + globals.events.on('ava-run', function (options) { + runner.run(options).then(exit); + }); + + globals.events.on('ava-init-exit', function () { + exit(); + }); +}); + +module.exports = runner.test; + +// TypeScript imports the `default` property for +// an ES2015 default import (`import test from 'ava'`) +// See: https://github.com/Microsoft/TypeScript/issues/2242#issuecomment-83694181 +module.exports.default = runner.test; diff --git a/browser.js b/browser.js new file mode 100644 index 000000000..c65def431 --- /dev/null +++ b/browser.js @@ -0,0 +1,49 @@ +'use strict'; + +var arrify = require('arrify'); +var globals = require('./lib/globals'); +var Api = require('./api'); + +var state = require('./state'); +var files = state.files; +var conf = state.conf; +var cli = state.cli; + +var api = new Api({ + failFast: cli.flags.failFast, + serial: cli.flags.serial, + babelEnabled: false, + cacheEnabled: false, + explicitTitles: false, + match: arrify(cli.flags.match), + babelConfig: conf.babel, + timeout: cli.flags.timeout, + concurrency: cli.flags.concurrency ? parseInt(cli.flags.concurrency, 10) : 0 +}); + +api.on('test-run', function (runStatus) { + runStatus.on('test', function (test) { + console.log('test'); + console.log(test); + }); + + // runStatus.on('error', logger.unhandledError); + + // runStatus.on('stdout', logger.stdout); + // runStatus.on('stderr', logger.stderr); +}); + +api.run(files) + .then(function (runStatus) { + console.log('finish'); + console.log(runStatus); + // logger.finish(runStatus); + // logger.exit(runStatus.failCount > 0 || runStatus.rejectionCount > 0 || runStatus.exceptionCount > 0 ? 1 : 0); + }) + .catch(function (err) { + // Don't swallow exceptions. Note that any expected error should already + // have been logged. + globals.setImmediate(function () { + throw err; + }); + }); diff --git a/cli.js b/cli.js index d4b313c21..8670b35c2 100755 --- a/cli.js +++ b/cli.js @@ -17,6 +17,9 @@ if (debug.enabled) { require('time-require'); } +var fs = require('fs'); +var uniqueTempDir = require('unique-temp-dir'); +var findCacheDir = require('find-cache-dir'); var updateNotifier = require('update-notifier'); var figures = require('figures'); var arrify = require('arrify'); @@ -26,10 +29,13 @@ var pkgConf = require('pkg-conf'); var chalk = require('chalk'); var isCi = require('is-ci'); var hasFlag = require('has-flag'); +var through = require('through'); var colors = require('./lib/colors'); +var CachingPrecompiler = require('./lib/caching-precompiler'); var verboseReporter = require('./lib/reporters/verbose'); var miniReporter = require('./lib/reporters/mini'); var tapReporter = require('./lib/reporters/tap'); +var AvaFiles = require('./lib/ava-files'); var Logger = require('./lib/logger'); var Watcher = require('./lib/watcher'); var Api = require('./api'); @@ -97,7 +103,8 @@ var cli = meow([ 'verbose', 'serial', 'tap', - 'watch' + 'watch', + 'browser' ], default: conf, alias: { @@ -109,7 +116,8 @@ var cli = meow([ w: 'watch', S: 'source', T: 'timeout', - c: 'concurrency' + c: 'concurrency', + b: 'browser' } }); @@ -128,6 +136,74 @@ if ( process.exit(1); } +var patterns = cli.input.length ? cli.input : arrify(conf.files); +var files = new AvaFiles(patterns) + .findTestFilesSync(); + +if (cli.flags.browser) { + var browserify = require('browserify'); + + var cacheDirPath = findCacheDir({name: 'ava', files: files}) || uniqueTempDir(); + var precompiler = new CachingPrecompiler(cacheDirPath, cli.flags.babelConfig); + + function precompile (file) { + if (files.indexOf(file) < 0) { + return through(); + } + + precompiler.precompileFile(file); + + var hash = precompiler.fileHashes[file]; + var path = __dirname + '/node_modules/.cache/ava/' + hash + '.js'; + + var fileStream = fs.createReadStream(path); + var transformStream = through(function () {}, function () {}); + + fileStream.on('data', function (data) { + transformStream.queue(data); + }); + + fileStream.on('end', function () { + transformStream.queue(null); + }); + + return transformStream; + } + + var state = { + files: files, + conf: conf, + cli: cli + }; + + var statePath = cacheDirPath + '/state.json'; + fs.writeFileSync(statePath, JSON.stringify(state), 'utf8'); + + var browserBundle = browserify(['browser.js'], { + fullPaths: true, + insertGlobals: true + }); + + browserBundle.require(statePath, { expose: './state' }); + + var browserStream = fs.createWriteStream('tests.js'); + browserBundle.bundle().pipe(browserStream); + + var workerBundle = browserify(['lib/browser-test-worker.js'], { + fullPaths: true, + insertGlobals: true + }); + + workerBundle.transform(precompile); + workerBundle.require(arrify(cli.flags.require)); + workerBundle.require(files); + + var workerStream = fs.createWriteStream('test-worker.js'); + workerBundle.bundle().pipe(workerStream); + + return; +} + var api = new Api({ failFast: cli.flags.failFast, serial: cli.flags.serial, @@ -164,11 +240,9 @@ api.on('test-run', function (runStatus) { runStatus.on('stderr', logger.stderr); }); -var files = cli.input.length ? cli.input : arrify(conf.files); - if (cli.flags.watch) { try { - var watcher = new Watcher(logger, api, files, arrify(cli.flags.source)); + var watcher = new Watcher(logger, api, patterns, arrify(cli.flags.source)); watcher.observeStdin(process.stdin); } catch (err) { if (err.name === 'AvaError') { diff --git a/lib/ava-files.js b/lib/ava-files.js index ef4938942..b3daeb194 100644 --- a/lib/ava-files.js +++ b/lib/ava-files.js @@ -49,6 +49,15 @@ AvaFiles.prototype.findTestFiles = function () { }); }; +AvaFiles.prototype.findTestFilesSync = function () { + return handlePathsSync(this.files, this.excludePatterns, { + cache: Object.create(null), + statCache: Object.create(null), + realpathCache: Object.create(null), + symlinks: Object.create(null) + }); +}; + function getDefaultIgnorePatterns() { return defaultIgnore.map(function (dir) { return dir + '/**/*'; @@ -257,6 +266,67 @@ function handlePaths(files, excludePatterns, globOptions) { }); } +function handlePathsSync(files, excludePatterns, globOptions) { + files = globby.sync(files.concat(excludePatterns), globOptions); + + var searchedParents = Object.create(null); + var foundFiles = Object.create(null); + + function alreadySearchingParent(dir) { + if (searchedParents[dir]) { + return true; + } + + var parentDir = path.dirname(dir); + + if (parentDir === dir) { + // We have reached the root path. + return false; + } + + return alreadySearchingParent(parentDir); + } + + var result = files + .map(function (file) { + if (fs.statSync(file).isDirectory()) { + if (alreadySearchingParent(file)) { + return null; + } + + searchedParents[file] = true; + + var pattern = path.join(file, '**', '*.js'); + + if (process.platform === 'win32') { + // Always use / in patterns, harmonizing matching across platforms. + pattern = slash(pattern); + } + + return handlePathsSync([pattern], excludePatterns, globOptions); + } + + // globby returns slashes even on Windows. Normalize here so the file + // paths are consistently platform-accurate as tests are run. + return path.normalize(file); + }); + + result = flatten(result); + + return result + .filter(function (file) { + return file && path.extname(file) === '.js' && path.basename(file)[0] !== '_'; + }) + .map(function (file) { + return path.resolve(file); + }) + .filter(function (file) { + var alreadyFound = foundFiles[file]; + foundFiles[file] = true; + return !alreadyFound; + }); +} + module.exports = AvaFiles; module.exports.defaultIncludePatterns = defaultIncludePatterns; module.exports.defaultExcludePatterns = defaultExcludePatterns; diff --git a/lib/browser-fork.js b/lib/browser-fork.js new file mode 100644 index 000000000..c49b5c317 --- /dev/null +++ b/lib/browser-fork.js @@ -0,0 +1,138 @@ +'use strict'; +var EventEmitter = require('events').EventEmitter; +var path = require('path'); +var objectAssign = require('object-assign'); +var Promise = require('bluebird'); +var debug = require('debug')('ava'); +var AvaError = require('./ava-error'); +var send = require('./send'); + +var env = process.env; + +// ensure NODE_PATH paths are absolute +if (env.NODE_PATH) { + env = objectAssign({}, env); + + env.NODE_PATH = env.NODE_PATH + .split(path.delimiter) + .map(function (x) { + return path.resolve(x); + }) + .join(path.delimiter); +} + +module.exports = function (file, opts) { + opts = objectAssign({ + file: file + }, opts); + + var relFile = path.relative('.', file); + + var testResults = []; + var results; + + var worker = new Worker('test-worker.js'); + var events = new EventEmitter(); + + var promise = new Promise(function (resolve, reject) { + worker.addEventListener('error', reject, false); + + events.on('exit', function () { + if (results) { + resolve(results); + } else { + reject(new AvaError('Test results were not received from ' + relFile)); + } + }); + + events.on('no-tests', function (data) { + send(worker, 'teardown'); + + var message = 'No tests found in ' + relFile; + + if (!data.avaRequired) { + message += ', make sure to import "ava" at the top of your test file'; + } + + reject(new AvaError(message)); + }); + }); + + worker.addEventListener('message', function (e) { + var event = e.data; + + if (!event.ava) { + return; + } + + event.name = event.name.replace(/^ava\-/, ''); + event.data.file = file; + + debug('ipc %s:\n%o', event.name, event.data); + + events.emit(event.name, event.data); + }, false); + + events.on('test', function (props) { + testResults.push(props); + }); + + events.on('results', function (data) { + results = data; + data.tests = testResults; + + send(worker, 'teardown'); + }); + + // teardown finished, now exit + events.on('teardown', function () { + send(worker, 'exit'); + }); + + // uncaught exception in fork, need to exit + events.on('uncaughtException', function () { + send(worker, 'teardown'); + }); + + promise.on = function () { + events.on.apply(events, arguments); + + return promise; + }; + + promise.send = function (name, data) { + send(worker, name, data); + + return promise; + }; + + promise.exit = function () { + send(worker, 'init-exit'); + + return promise; + }; + + // send 'run' event only when fork is listening for it + var isReady = false; + + events.on('stats', function () { + isReady = true; + }); + + promise.run = function (options) { + if (isReady) { + send(worker, 'run', options); + return promise; + } + + events.on('stats', function () { + send(worker, 'run', options); + }); + + return promise; + }; + + send(worker, 'init', opts); + + return promise; +}; diff --git a/lib/browser-send.js b/lib/browser-send.js new file mode 100644 index 000000000..c60fc6739 --- /dev/null +++ b/lib/browser-send.js @@ -0,0 +1,18 @@ +'use strict'; + +// utility to send messages to processes +module.exports = function (worker, name, data) { + if (typeof worker === 'string') { + data = name || {}; + name = worker; + worker = self; + } + + var event = { + name: 'ava-' + name, + data: data, + ava: true + }; + + worker.postMessage(event); +}; diff --git a/lib/browser-test-worker.js b/lib/browser-test-worker.js new file mode 100644 index 000000000..7e8461dc7 --- /dev/null +++ b/lib/browser-test-worker.js @@ -0,0 +1,87 @@ +'use strict'; + +var EventEmitter = require('events').EventEmitter; +var path = require('path'); +var Promise = require('bluebird'); // eslint-disable-line +var debug = require('debug')('ava'); +var send = require('./send'); + +// bind globals first before anything has a chance to interfere +var globals = require('./globals'); + +var events = globals.events = new EventEmitter(); + +// Bluebird specific +Promise.longStackTraces(); + +// var loudRejection = require('loud-rejection/api')(process); // eslint-disable-line +var serializeError = require('./serialize-error'); +// var throwsHelper = require('./throws-helper'); + +// check if test files required ava and show error, when they didn't +exports.avaRequired = false; + +events.on('ava-init', function (options) { + globals.options = options; + + var testPath = options.file; + require(testPath); + + // if ava was not required, show an error + if (!exports.avaRequired) { + send('no-tests', {avaRequired: false}); + } +}); + +// process.on('unhandledRejection', throwsHelper); + +// process.on('uncaughtException', function (exception) { +// throwsHelper(exception); +// send('uncaughtException', {exception: serializeError(exception)}); +// }); + +// parse and re-emit ava messages +self.addEventListener('message', function (e) { + var event = e.data; + + if (!event || !event.ava) { + return; + } + + events.emit(event.name, event.data); +}, false); + +events.on('ava-exit', function () { + exit(); +}); + +var tearingDown = false; +events.on('ava-teardown', function () { + // ava-teardown can be sent more than once. + if (tearingDown) { + return; + } + tearingDown = true; + + // var rejections = loudRejection.currentlyUnhandled(); + + // if (rejections.length === 0) { + // exit(); + // return; + // } + + // rejections = rejections.map(function (rejection) { + // return serializeError(rejection.reason); + // }); + + // send('unhandledRejections', {rejections: rejections}); + // globals.setTimeout(exit, 100); + + exit(); +}); + +function exit() { + send('exit'); + + globals.setTimeout(close, 100); +} diff --git a/lib/globals.js b/lib/globals.js index cfcaf146f..1b864e3e0 100644 --- a/lib/globals.js +++ b/lib/globals.js @@ -6,9 +6,16 @@ var x = module.exports; x.now = Date.now; -x.setTimeout = setTimeout; +// .call(null) prevents Illegal Invocation error in browser +var _setTimeout = setTimeout; +x.setTimeout = function (fn, delay) { + _setTimeout.call(null, fn, delay); +}; -x.clearTimeout = clearTimeout; +var _clearTimeout = clearTimeout; +x.clearTimeout = function (timer) { + _clearTimeout.call(null, timer); +}; x.setImmediate = require('set-immediate-shim'); diff --git a/lib/test-worker.js b/lib/test-worker.js index 8940391c4..96c2c0d46 100644 --- a/lib/test-worker.js +++ b/lib/test-worker.js @@ -71,6 +71,10 @@ var cacheDir = opts.cacheDir; exports.avaRequired = false; installPrecompiler(function (filename) { + if (!opts.precompiled) { + return null; + } + var precompiled = opts.precompiled[filename]; if (precompiled) { diff --git a/lib/watcher.js b/lib/watcher.js index a2ae9569d..b7d02e6b5 100644 --- a/lib/watcher.js +++ b/lib/watcher.js @@ -24,9 +24,9 @@ function rethrowAsync(err) { }); } -function Watcher(logger, api, files, sources) { +function Watcher(logger, api, patterns, sources) { this.debouncer = new Debouncer(this); - this.avaFiles = new AvaFiles(files, sources); + this.avaFiles = new AvaFiles(patterns, sources); this.isTest = this.avaFiles.makeTestMatcher(); @@ -65,7 +65,11 @@ function Watcher(logger, api, files, sources) { } var self = this; - this.busy = api.run(specificFiles || files, { + + var files = new AvaFiles(specificFiles || patterns) + .findTestFilesSync(); + + this.busy = api.run(files, { runOnlyExclusive: runOnlyExclusive }).then(function (runStatus) { runStatus.previousFailCount = self.sumPreviousFailures(currentVector); diff --git a/package.json b/package.json index 72156fa58..6a5b055f8 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,12 @@ "*.js", "index.d.ts" ], + "browser": { + "./lib/fork.js": "./lib/browser-fork.js", + "./lib/send.js": "./lib/browser-send.js", + "./index.js": "./browser-index.js", + "./lib/test-worker.js": "./lib/browser-test-worker.js" + }, "keywords": [ "test", "runner", @@ -93,6 +99,7 @@ "babel-preset-stage-2": "^6.3.13", "babel-runtime": "^6.3.19", "bluebird": "^3.0.0", + "browserify": "^13.0.1", "caching-transform": "^1.0.0", "chalk": "^1.0.0", "clean-yaml-object": "^0.1.0", @@ -144,6 +151,7 @@ "stack-utils": "^0.4.0", "strip-ansi": "^3.0.1", "strip-bom": "^2.0.0", + "through": "^2.3.8", "time-require": "^0.1.2", "unique-temp-dir": "^1.0.0", "update-notifier": "^0.7.0" diff --git a/test/api.js b/test/api.js index 9201f4a06..3d0bd6a20 100644 --- a/test/api.js +++ b/test/api.js @@ -4,6 +4,7 @@ var fs = require('fs'); var figures = require('figures'); var rimraf = require('rimraf'); var test = require('tap').test; +var AvaFiles = require('../lib/ava-files'); var Api = require('../api'); var testCapitalizerPlugin = require('./fixture/babel-plugin-test-capitalizer'); @@ -118,13 +119,6 @@ function generateTests(prefix, apiCreator) { var api = apiCreator(); - api.run(files) - .then(function () { - // if all lines were removed from expected output - // actual output matches expected output - t.is(expected.length, 0); - }); - api.on('test-run', function (runStatus) { runStatus.on('test', function (a) { index = expected.indexOf(a.title); @@ -135,6 +129,13 @@ function generateTests(prefix, apiCreator) { expected.splice(index, 1); }); }); + + api.run(files) + .then(function () { + // if all lines were removed from expected output + // actual output matches expected output + t.is(expected.length, 0); + }); }); test(prefix + 'test title prefixes — single file', function (t) { @@ -151,13 +152,6 @@ function generateTests(prefix, apiCreator) { var api = apiCreator(); - api.run(files) - .then(function () { - // if all lines were removed from expected output - // actual output matches expected output - t.is(expected.length, 0); - }); - api.on('test-run', function (runStatus) { runStatus.on('test', function (a) { index = expected.indexOf(a.title); @@ -168,6 +162,13 @@ function generateTests(prefix, apiCreator) { expected.splice(index, 1); }); }); + + api.run(files) + .then(function () { + // if all lines were removed from expected output + // actual output matches expected output + t.is(expected.length, 0); + }); }); test(prefix + 'test title prefixes — single file (explicit)', function (t) { @@ -186,13 +187,6 @@ function generateTests(prefix, apiCreator) { explicitTitles: true }); - api.run(files) - .then(function () { - // if all lines were removed from expected output - // actual output matches expected output - t.is(expected.length, 0); - }); - api.on('test-run', function (runStatus) { runStatus.on('test', function (a) { index = expected.indexOf(a.title); @@ -203,6 +197,13 @@ function generateTests(prefix, apiCreator) { expected.splice(index, 1); }); }); + + api.run(files) + .then(function () { + // if all lines were removed from expected output + // actual output matches expected output + t.is(expected.length, 0); + }); }); test(prefix + 'display filename prefixes for failed test stack traces', function (t) { @@ -479,18 +480,6 @@ function generateTests(prefix, apiCreator) { }); }); - test(prefix + 'search directories recursively for files', function (t) { - t.plan(2); - - var api = apiCreator(); - - api.run([path.join(__dirname, 'fixture/subdir')]) - .then(function (result) { - t.is(result.passCount, 2); - t.is(result.failCount, 1); - }); - }); - test(prefix + 'titles of both passing and failing tests and AssertionErrors are returned', function (t) { t.plan(3); @@ -565,66 +554,6 @@ function generateTests(prefix, apiCreator) { return api.run([path.join(__dirname, 'fixture/immediate-3-exit.js')]); }); - test(prefix + 'testing nonexistent files causes an AvaError to be emitted', function (t) { - t.plan(2); - - var api = apiCreator(); - - api.on('test-run', function (runStatus) { - runStatus.on('error', function (err) { - t.is(err.name, 'AvaError'); - t.match(err.message, /Couldn't find any files to test/); - }); - }); - - return api.run([path.join(__dirname, 'fixture/broken.js')]); - }); - - test(prefix + 'test file in node_modules is ignored', function (t) { - t.plan(2); - - var api = apiCreator(); - - api.on('test-run', function (runStatus) { - runStatus.on('error', function (err) { - t.is(err.name, 'AvaError'); - t.match(err.message, /Couldn't find any files to test/); - }); - }); - - return api.run([path.join(__dirname, 'fixture/ignored-dirs/node_modules/test.js')]); - }); - - test(prefix + 'test file in fixtures is ignored', function (t) { - t.plan(2); - - var api = apiCreator(); - - api.on('test-run', function (runStatus) { - runStatus.on('error', function (err) { - t.is(err.name, 'AvaError'); - t.match(err.message, /Couldn't find any files to test/); - }); - }); - - return api.run([path.join(__dirname, 'fixture/ignored-dirs/fixtures/test.js')]); - }); - - test(prefix + 'test file in helpers is ignored', function (t) { - t.plan(2); - - var api = apiCreator(); - - api.on('test-run', function (runStatus) { - runStatus.on('error', function (err) { - t.is(err.name, 'AvaError'); - t.match(err.message, /Couldn't find any files to test/); - }); - }); - - return api.run([path.join(__dirname, 'fixture/ignored-dirs/helpers/test.js')]); - }); - test(prefix + 'Node.js-style --require CLI argument', function (t) { t.plan(1); @@ -760,7 +689,8 @@ function generateTests(prefix, apiCreator) { runStatus.on('error', function () {}); }); - var result = api.run(['test/fixture/with-dependencies/*test*.js']); + var files = new AvaFiles(['test/fixture/with-dependencies/*test*.js']).findTestFilesSync(); + var result = api.run(files); return result.catch(function () {}); }); @@ -782,8 +712,8 @@ function generateTests(prefix, apiCreator) { }); return api.run([ - 'test/fixture/exclusive.js', - 'test/fixture/generators.js' + path.resolve('test/fixture/exclusive.js'), + path.resolve('test/fixture/generators.js') ]); }); diff --git a/test/ava-files.js b/test/ava-files.js index 4426b769c..4f6d807a6 100644 --- a/test/ava-files.js +++ b/test/ava-files.js @@ -141,3 +141,18 @@ test('findFiles - finds the correct files by default', function (t) { t.end(); }); }); + +test('findFiles - test file in node_modules, fixtures and helpers is ignored', function (t) { + var fixtureDir = fixture('ignored-dirs'); + process.chdir(fixtureDir); + + var expected = []; + + var avaFiles = new AvaFiles(); + avaFiles + .findTestFiles() + .then(function (files) { + t.deepEqual(files, expected); + t.end(); + }); +}); diff --git a/test/fixture/ignored-dirs/fixtures/test.js b/test/fixture/ava-files/ignored-dirs/fixtures/test.js similarity index 100% rename from test/fixture/ignored-dirs/fixtures/test.js rename to test/fixture/ava-files/ignored-dirs/fixtures/test.js diff --git a/test/fixture/ignored-dirs/helpers/test.js b/test/fixture/ava-files/ignored-dirs/helpers/test.js similarity index 100% rename from test/fixture/ignored-dirs/helpers/test.js rename to test/fixture/ava-files/ignored-dirs/helpers/test.js diff --git a/test/watcher.js b/test/watcher.js index 993fd2729..97cc4dba6 100644 --- a/test/watcher.js +++ b/test/watcher.js @@ -115,15 +115,20 @@ group('chokidar is installed', function (beforeEach, test, group) { logger.clear.returns(true); - avaFiles = AvaFiles; - - api.run.returns(new Promise(function () {})); files = [ 'test.js', 'test-*.js', 'test' ]; + avaFiles = AvaFiles; + + avaFiles.prototype.findTestFilesSync = function () { + return this.files; + }; + + api.run.returns(new Promise(function () {})); + resetRunStatus(); stdin = new PassThrough();