From 33b65a183803dbe46609e2d62fb144f3bf29be85 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Wed, 21 Sep 2016 09:17:38 -0500 Subject: [PATCH 1/7] Use current working directory if no `package.json` --- cli.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cli.js b/cli.js index 26a0571a2..6ad7112df 100755 --- a/cli.js +++ b/cli.js @@ -43,7 +43,13 @@ Promise.longStackTraces(); var conf = pkgConf.sync('ava'); -var pkgDir = path.dirname(pkgConf.filepath(conf)); +var pkgDir; + +try { + pkgDir = path.dirname(pkgConf.filepath(conf)); +} catch (err) { + pkgDir = process.cwd(); +} try { conf.babel = babelConfig.validate(conf.babel); From 06890db24798ad185f2e28afcdfc79caf1346e02 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 22 Sep 2016 09:08:50 -0500 Subject: [PATCH 2/7] Check for `filepath` instead --- cli.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cli.js b/cli.js index 6ad7112df..133e2b1f7 100755 --- a/cli.js +++ b/cli.js @@ -44,11 +44,12 @@ Promise.longStackTraces(); var conf = pkgConf.sync('ava'); var pkgDir; +var filepath = pkgConf.filepath(conf) -try { - pkgDir = path.dirname(pkgConf.filepath(conf)); -} catch (err) { +if (!filepath) { pkgDir = process.cwd(); +} else { + pkgDir = path.dirname(filepath) } try { From 60a20a752aa52ea7ec9bc2310282015531cc3c9c Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 22 Sep 2016 10:07:54 -0500 Subject: [PATCH 3/7] Fix linting errors --- cli.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli.js b/cli.js index 133e2b1f7..f7ba61987 100755 --- a/cli.js +++ b/cli.js @@ -44,12 +44,12 @@ Promise.longStackTraces(); var conf = pkgConf.sync('ava'); var pkgDir; -var filepath = pkgConf.filepath(conf) +var filepath = pkgConf.filepath(conf); -if (!filepath) { +if (filepath === null) { pkgDir = process.cwd(); } else { - pkgDir = path.dirname(filepath) + pkgDir = path.dirname(filepath); } try { From 1c3f1151e2781fbff73af1c3a18604826dc38580 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Thu, 22 Sep 2016 14:05:05 -0500 Subject: [PATCH 4/7] Use ternary --- cli.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cli.js b/cli.js index f7ba61987..16d2163ab 100755 --- a/cli.js +++ b/cli.js @@ -43,14 +43,8 @@ Promise.longStackTraces(); var conf = pkgConf.sync('ava'); -var pkgDir; var filepath = pkgConf.filepath(conf); - -if (filepath === null) { - pkgDir = process.cwd(); -} else { - pkgDir = path.dirname(filepath); -} +var pkgDir = filepath === null ? process.cwd() : path.dirname(filepath); try { conf.babel = babelConfig.validate(conf.babel); From fc6d3d7308dbf0e99a17fee46cbc0929b1172dd7 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Mon, 10 Oct 2016 10:44:52 -0400 Subject: [PATCH 5/7] Add test --- test/cli.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/cli.js b/test/cli.js index 4d50f53af..e0c138442 100644 --- a/test/cli.js +++ b/test/cli.js @@ -1,5 +1,6 @@ 'use strict'; var path = require('path'); +var fs = require('fs'); var childProcess = require('child_process'); var test = require('tap').test; global.Promise = require('bluebird'); @@ -10,6 +11,8 @@ var chalk = require('chalk'); var touch = require('touch'); var proxyquire = require('proxyquire'); var sinon = require('sinon'); +var uniqueTempDir = require('unique-temp-dir'); +var execa = require('execa'); var cliPath = path.join(__dirname, '../cli.js'); @@ -368,3 +371,17 @@ test('prefers local version of ava', function (t) { t.ok(debugSpy.calledWith('Using local install of AVA')); t.end(); }); + +test('use current working directory if `package.json` is not found', function (t) { + var dir = uniqueTempDir({create: true}); + var testFilePath = path.join(dir, 'test.js'); + var cliPath = require.resolve('../cli.js'); + var avaPath = require.resolve('../index.js'); + + fs.writeFileSync(testFilePath, 'import test from \'' + avaPath + '\';\ntest(t => { t.pass(); });'); + + execa(process.execPath, [cliPath], {cwd: dir}).then(() => { + t.pass(); + t.end(); + }); +}); From d2d6bf4254c28df4e8d21ab374b0b510bc1a1909 Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Mon, 10 Oct 2016 16:53:03 -0400 Subject: [PATCH 6/7] Fix breaking builds Update test Document more async pitfalls (#1045) * Use tabs in pitfalls code blocks * Spell out async in pitfals doc * Promote async/await in pitfalls doc * Provide example of using pify * Suggest pify for catching exceptions * Suggest promisifying callback-functions for better exception catching Do not say that `t` is the only argument of a test bump all the `babel` related modules to latest Some older versions that are still matched by semver were buggy and some users were somehow getting those versions. Maybe from a stale npm cache. Fixes #1058 bump `tap` and `nyc` highlights - Automatic migration from other test runners Thanks @skovhus! :) Replace `jsdom` with `browser-env` in browser recipe (#1054) Clean up API (#1061) Switch to `lodash.isEqual` for deep quality check (#1063) Add link to first contribution blog post Reduce transpilation on Node.js >=4 (#1068) Better startup performance and improved stack traces. Remove --require CLI option (#1070) Fixes #1069. Fix link to configuration in readme (#1073) make XO happy Locking the version as 0.17.0 requires Node.js 4 Check whether a cache dir was found before attempting to use it AVA will never find a cache dir if it is run in a directory without a package.json file. --- api.js | 475 +++++++++--------- bench/run.js | 2 +- cli.js | 10 +- contributing.md | 2 + docs/common-pitfalls.md | 42 +- docs/recipes/browser-testing.md | 39 +- docs/recipes/code-coverage.md | 2 - docs/recipes/watch-mode.md | 4 +- lib/assert.js | 2 +- lib/babel-config.js | 7 +- lib/caching-precompiler.js | 3 +- lib/process-adapter.js | 2 +- lib/reporters/mini.js | 6 +- lib/test-worker.js | 3 +- lib/throws-helper.js | 2 +- package.json | 38 +- profile.js | 7 +- readme.md | 6 +- test/api.js | 2 +- test/assert.js | 84 +++- test/cli.js | 15 +- test/fixture/pkg-conf/pkg-overrides/actual.js | 3 - test/fixture/pkg-conf/precedence/c.js | 3 - test/runner.js | 2 +- test/test.js | 2 +- test/watcher.js | 2 +- 26 files changed, 435 insertions(+), 330 deletions(-) diff --git a/api.js b/api.js index 3033c7f7d..aebdf7945 100644 --- a/api.js +++ b/api.js @@ -2,21 +2,47 @@ var EventEmitter = require('events').EventEmitter; var path = require('path'); var util = require('util'); -var Promise = require('bluebird'); -var objectAssign = require('object-assign'); var commonPathPrefix = require('common-path-prefix'); -var resolveCwd = require('resolve-cwd'); var uniqueTempDir = require('unique-temp-dir'); var findCacheDir = require('find-cache-dir'); +var objectAssign = require('object-assign'); +var resolveCwd = require('resolve-cwd'); var debounce = require('lodash.debounce'); -var ms = require('ms'); var AvaFiles = require('ava-files'); var autoBind = require('auto-bind'); +var Promise = require('bluebird'); var getPort = require('get-port'); -var AvaError = require('./lib/ava-error'); -var fork = require('./lib/fork'); +var arrify = require('arrify'); +var ms = require('ms'); var CachingPrecompiler = require('./lib/caching-precompiler'); var RunStatus = require('./lib/run-status'); +var AvaError = require('./lib/ava-error'); +var fork = require('./lib/fork'); + +function resolveModules(modules) { + return arrify(modules).map(function (name) { + var modulePath = resolveCwd(name); + if (modulePath === null) { + throw new Error('Could not resolve required module \'' + name + '\''); + } + + return modulePath; + }); +} + +function getBlankResults() { + return { + stats: { + knownFailureCount: 0, + testCount: 0, + passCount: 0, + skipCount: 0, + todoCount: 0, + failCount: 0 + }, + tests: [] + }; +} function Api(options) { if (!(this instanceof Api)) { @@ -24,6 +50,7 @@ function Api(options) { } EventEmitter.call(this); + autoBind(this); this.options = objectAssign({ cwd: process.cwd(), @@ -31,16 +58,7 @@ function Api(options) { match: [] }, options); - this.options.require = (this.options.require || []).map(function (moduleId) { - var ret = resolveCwd(moduleId); - if (ret === null) { - throw new Error('Could not resolve required module \'' + moduleId + '\''); - } - - return ret; - }); - - autoBind(this); + this.options.require = resolveModules(this.options.require); } util.inherits(Api, EventEmitter); @@ -56,104 +74,124 @@ Api.prototype._runFile = function (file, runStatus, execArgv) { }); var emitter = fork(file, options, execArgv); - runStatus.observeFork(emitter); return emitter; }; +Api.prototype.run = function (files, options) { + var self = this; + + return new AvaFiles({cwd: this.options.resolveTestsFrom, files: files}) + .findTestFiles() + .then(function (files) { + return self._run(files, options); + }); +}; + Api.prototype._onTimeout = function (runStatus) { var timeout = ms(this.options.timeout); - var message = 'Exited because no new tests completed within the last ' + timeout + 'ms of inactivity'; - - runStatus.handleExceptions({ - exception: new AvaError(message), - file: undefined - }); + var err = new AvaError('Exited because no new tests completed within the last ' + timeout + 'ms of inactivity'); + this._handleError(runStatus, err); runStatus.emit('timeout'); }; -Api.prototype.run = function (files, options) { +Api.prototype._setupTimeout = function (runStatus) { var self = this; + var timeout = ms(this.options.timeout); - return new AvaFiles({files: files, cwd: this.options.resolveTestsFrom}) - .findTestFiles() - .then(function (files) { - return self._run(files, options); + runStatus._restartTimer = debounce(function () { + self._onTimeout(runStatus); + }, timeout); + + runStatus._restartTimer(); + runStatus.on('test', runStatus._restartTimer); +}; + +Api.prototype._cancelTimeout = function (runStatus) { + runStatus._restartTimer.cancel(); +}; + +Api.prototype._setupPrecompiler = function (files) { + var isCacheEnabled = this.options.cacheEnabled !== false; + var cacheDir = uniqueTempDir(); + + if (isCacheEnabled) { + var foundDir = findCacheDir({ + name: 'ava', + files: files }); + if (foundDir !== null) { + cacheDir = foundDir; + } + } + + this.options.cacheDir = cacheDir; + + var isPowerAssertEnabled = this.options.powerAssert !== false; + this.precompiler = new CachingPrecompiler(cacheDir, this.options.babelConfig, isPowerAssertEnabled); }; -Api.prototype._run = function (files, _options) { - var self = this; +Api.prototype._run = function (files, options) { + options = options || {}; + var runStatus = new RunStatus({ + runOnlyExclusive: options.runOnlyExclusive, prefixTitles: this.options.explicitTitles || files.length > 1, - runOnlyExclusive: _options && _options.runOnlyExclusive, base: path.relative('.', commonPathPrefix(files)) + path.sep }); - if (self.options.timeout) { - var timeout = ms(self.options.timeout); - runStatus._restartTimer = debounce(function () { - self._onTimeout(runStatus); - }, timeout); - runStatus._restartTimer(); - runStatus.on('test', runStatus._restartTimer); - } - - self.emit('test-run', runStatus, files); + this.emit('test-run', runStatus, files); if (files.length === 0) { - runStatus.handleExceptions({ - exception: new AvaError('Couldn\'t find any files to test'), - file: undefined - }); + var err = new AvaError('Couldn\'t find any files to test'); + this._handleError(runStatus, err); return Promise.resolve(runStatus); } - var cacheEnabled = self.options.cacheEnabled !== false; - var cacheDir = (cacheEnabled && findCacheDir({ - name: 'ava', - files: files - })) || uniqueTempDir(); + this._setupPrecompiler(files); - self.options.cacheDir = cacheDir; - self.precompiler = new CachingPrecompiler(cacheDir, self.options.babelConfig, self.options.powerAssert); - self.fileCount = files.length; + if (this.options.timeout) { + this._setupTimeout(runStatus); + } var overwatch; if (this.options.concurrency > 0) { - overwatch = this._runLimitedPool(files, runStatus, self.options.serial ? 1 : this.options.concurrency); + var concurrency = this.options.serial ? 1 : this.options.concurrency; + overwatch = this._runWithPool(files, runStatus, concurrency); } else { - // _runNoPool exists to preserve legacy behavior, specifically around `.only` - overwatch = this._runNoPool(files, runStatus); + // _runWithoutPool exists to preserve legacy behavior, specifically around `.only` + overwatch = this._runWithoutPool(files, runStatus); } return overwatch; }; -Api.prototype.computeForkExecArgs = function (files) { +Api.prototype._computeForkExecArgs = function (files) { var execArgv = this.options.testOnlyExecArgv || process.execArgv; var debugArgIndex = -1; // --debug-brk is used in addition to --inspect to break on first line and wait execArgv.some(function (arg, index) { - if (arg === '--inspect' || arg.indexOf('--inspect=') === 0) { + var isDebugArg = arg === '--inspect' || arg.indexOf('--inspect=') === 0; + if (isDebugArg) { debugArgIndex = index; - return true; } - return false; + + return isDebugArg; }); - var isInspect = debugArgIndex !== -1; + var isInspect = debugArgIndex >= 0; if (!isInspect) { execArgv.some(function (arg, index) { - if (arg === '--debug' || arg === '--debug-brk' || arg.indexOf('--debug-brk=') === 0 || arg.indexOf('--debug=') === 0) { + var isDebugArg = arg === '--debug' || arg === '--debug-brk' || arg.indexOf('--debug-brk=') === 0 || arg.indexOf('--debug=') === 0; + if (isDebugArg) { debugArgIndex = index; - return true; } - return false; + + return isDebugArg; }); } @@ -161,204 +199,163 @@ Api.prototype.computeForkExecArgs = function (files) { return Promise.resolve([]); } - return Promise.map(files, getPort) - .then(function (ports) { - return ports.map(function (port) { - var forkExecArgv = execArgv.slice(); - var flagName = isInspect ? '--inspect' : '--debug'; - var oldValue = forkExecArgv[debugArgIndex]; - if (oldValue.indexOf('brk') > 0) { - flagName += '-brk'; - } - forkExecArgv[debugArgIndex] = flagName + '=' + port; - return forkExecArgv; - }); + return Promise + .map(files, getPort) + .map(function (port) { + var forkExecArgv = execArgv.slice(); + var flagName = isInspect ? '--inspect' : '--debug'; + var oldValue = forkExecArgv[debugArgIndex]; + if (oldValue.indexOf('brk') > 0) { + flagName += '-brk'; + } + + forkExecArgv[debugArgIndex] = flagName + '=' + port; + + return forkExecArgv; }); }; -Api.prototype._runNoPool = function (files, runStatus) { +Api.prototype._handleError = function (runStatus, err) { + runStatus.handleExceptions({ + exception: err, + file: err.file ? path.relative('.', err.file) : undefined + }); +}; + +Api.prototype._runWithoutPool = function (files, runStatus) { var self = this; - var tests = new Array(self.fileCount); - // TODO: thid should be cleared at the end of the run + var tests = []; + var execArgvList; + + // TODO: this should be cleared at the end of the run runStatus.on('timeout', function () { tests.forEach(function (fork) { fork.exit(); }); }); - return self.computeForkExecArgs(files) - .then(function (execArgvList) { + return this._computeForkExecArgs(files) + .then(function (argvList) { + execArgvList = argvList; + }) + .return(files) + .each(function (file, index) { return new Promise(function (resolve) { - function run() { - if (self.options.match.length > 0 && !runStatus.hasExclusive) { - runStatus.handleExceptions({ - exception: new AvaError('Couldn\'t find any matching tests'), - file: undefined - }); - - resolve([]); - return; - } - - var method = self.options.serial ? 'mapSeries' : 'map'; - var options = { - runOnlyExclusive: runStatus.hasExclusive - }; - - resolve(Promise[method](files, function (file, index) { - return tests[index].run(options).catch(function (err) { - // The test failed catastrophically. Flag it up as an - // exception, then return an empty result. Other tests may - // continue to run. - runStatus.handleExceptions({ - exception: err, - file: path.relative('.', file) - }); - - return getBlankResults(); - }); - })); - } - - // receive test count from all files and then run the tests - var unreportedFiles = self.fileCount; - var bailed = false; - - files.every(function (file, index) { - var tried = false; - - function tryRun() { - if (!tried && !bailed) { - tried = true; - unreportedFiles--; - - if (unreportedFiles === 0) { - run(); - } - } - } - - try { - var test = tests[index] = self._runFile(file, runStatus, execArgvList[index]); - - test.on('stats', tryRun); - test.catch(tryRun); - - return true; - } catch (err) { - bailed = true; - - runStatus.handleExceptions({ - exception: err, - file: path.relative('.', file) - }); - - resolve([]); - - return false; - } + var forkArgs = execArgvList[index]; + var test = self._runFile(file, runStatus, forkArgs); + tests.push(test); + + test.on('stats', resolve); + test.catch(resolve); + }).catch(function (err) { + err.results = []; + err.file = file; + return Promise.reject(err); + }); + }) + .then(function () { + if (self.options.match.length > 0 && !runStatus.hasExclusive) { + var err = new AvaError('Couldn\'t find any matching tests'); + err.file = undefined; + err.results = []; + + return Promise.reject(err); + } + + var method = self.options.serial ? 'mapSeries' : 'map'; + var options = { + runOnlyExclusive: runStatus.hasExclusive + }; + + return Promise[method](files, function (file, index) { + return tests[index].run(options).catch(function (err) { + err.file = file; + self._handleError(runStatus, err); + + return getBlankResults(); }); - }).then(function (results) { - if (results.length === 0) { - // No tests ran, make sure to tear down the child processes. - tests.forEach(function (test) { - test.send('teardown'); - }); - } - - return results; - }).then(function (results) { - // cancel debounced _onTimeout() from firing - if (self.options.timeout) { - runStatus._restartTimer.cancel(); - } - - runStatus.processResults(results); - return runStatus; }); + }) + .catch(function (err) { + self._handleError(runStatus, err); + + return err.results; + }) + .tap(function (results) { + // if no tests ran, make sure to tear down the child processes + if (results.length === 0) { + tests.forEach(function (test) { + test.send('teardown'); + }); + } + }) + .then(function (results) { + // cancel debounced _onTimeout() from firing + if (self.options.timeout) { + self._cancelTimeout(runStatus); + } + + runStatus.processResults(results); + + return runStatus; }); }; -function getBlankResults() { - return { - stats: { - testCount: 0, - passCount: 0, - knownFailureCount: 0, - skipCount: 0, - todoCount: 0, - failCount: 0 - }, - tests: [] - }; -} - -Api.prototype._runLimitedPool = function (files, runStatus, concurrency) { +Api.prototype._runWithPool = function (files, runStatus, concurrency) { var self = this; - var tests = {}; + + var tests = []; + var execArgvList; runStatus.on('timeout', function () { - Object.keys(tests).forEach(function (file) { - var fork = tests[file]; + tests.forEach(function (fork) { fork.exit(); }); }); - return self.computeForkExecArgs(files) - .then(function (execArgvList) { - return Promise.map(files, function (file, index) { - var handleException = function (err) { - runStatus.handleExceptions({ - exception: err, - file: path.relative('.', file) - }); - }; - - try { - var test = tests[file] = self._runFile(file, runStatus, execArgvList[index]); - - return new Promise(function (resolve, reject) { - var runner = function () { - var options = { - // If we're looking for matches, run every single test process in exclusive-only mode - runOnlyExclusive: self.options.match.length > 0 - }; - test.run(options) - .then(resolve) - .catch(reject); - }; - - test.on('stats', runner); - test.on('exit', function () { - delete tests[file]; - }); - test.catch(runner); - }).catch(handleException); - } catch (err) { - handleException(err); - } - }, {concurrency: concurrency}) - .then(function (results) { - // Filter out undefined results (usually result of caught exceptions) - results = results.filter(Boolean); - - // cancel debounced _onTimeout() from firing - if (self.options.timeout) { - runStatus._restartTimer.cancel(); - } - - if (self.options.match.length > 0 && !runStatus.hasExclusive) { - // Ensure results are empty - results = []; - runStatus.handleExceptions({ - exception: new AvaError('Couldn\'t find any matching tests'), - file: undefined - }); - } - - runStatus.processResults(results); - return runStatus; - }); + return this._computeForkExecArgs(files) + .then(function (argvList) { + execArgvList = argvList; + }) + .return(files) + .map(function (file, index) { + return new Promise(function (resolve) { + var forkArgs = execArgvList[index]; + var test = self._runFile(file, runStatus, forkArgs); + tests.push(test); + + // If we're looking for matches, run every single test process in exclusive-only mode + var options = { + runOnlyExclusive: self.options.match.length > 0 + }; + + resolve(test.run(options)); + }).catch(function (err) { + err.file = file; + self._handleError(runStatus, err); + + return getBlankResults(); }); + }, {concurrency: concurrency}) + .then(function (results) { + // Filter out undefined results (usually result of caught exceptions) + results = results.filter(Boolean); + + // cancel debounced _onTimeout() from firing + if (self.options.timeout) { + self._cancelTimeout(runStatus); + } + + if (self.options.match.length > 0 && !runStatus.hasExclusive) { + results = []; + + var err = new AvaError('Couldn\'t find any matching tests'); + self._handleError(runStatus, err); + } + + runStatus.processResults(results); + + return runStatus; + }); }; diff --git a/bench/run.js b/bench/run.js index 32484045f..204fee5e1 100644 --- a/bench/run.js +++ b/bench/run.js @@ -76,7 +76,7 @@ if (process.argv.length === 2) { } currentArgs.push(arg); }); - if (currentArgs.length) { + if (currentArgs.length > 0) { list.push({ args: currentArgs, shouldFail: shouldFail diff --git a/cli.js b/cli.js index 16d2163ab..2730451ef 100755 --- a/cli.js +++ b/cli.js @@ -61,7 +61,6 @@ var cli = meow([ ' --init Add AVA to your project', ' --fail-fast Stop after first test failure', ' --serial, -s Run tests serially', - ' --require, -r Module to preload (Can be repeated)', ' --tap, -t Generate TAP output', ' --verbose, -v Enable verbose output', ' --no-cache Disable the transpiler cache', @@ -85,7 +84,6 @@ var cli = meow([ ], { string: [ '_', - 'require', 'timeout', 'source', 'match', @@ -102,7 +100,6 @@ var cli = meow([ alias: { t: 'tap', v: 'verbose', - r: 'require', s: 'serial', m: 'match', w: 'watch', @@ -127,10 +124,15 @@ if ( process.exit(1); } +if (hasFlag('--require') || hasFlag('-r')) { + console.error(' ' + colors.error(figures.cross) + ' The --require and -r flags are deprecated. Requirements should be configured in package.json - see documentation.'); + process.exit(1); +} + var api = new Api({ failFast: cli.flags.failFast, serial: cli.flags.serial, - require: arrify(cli.flags.require), + require: arrify(conf.require), cacheEnabled: cli.flags.cache !== false, powerAssert: cli.flags.powerAssert !== false, explicitTitles: cli.flags.watch, diff --git a/contributing.md b/contributing.md index 153898d60..2300508f8 100644 --- a/contributing.md +++ b/contributing.md @@ -67,3 +67,5 @@ We have a [chat](https://gitter.im/avajs/ava). Jump in there and lurk, talk to u - You might be asked to do changes to your pull request. There's never a need to open another pull request. [Just update the existing one.](https://github.com/RichardLitt/docs/blob/master/amending-a-commit-guide.md) Note: when making code changes, try to remember AVA's mantra (stolen from Python) of having preferably one way to do something. For example, a request to add an alias to part of the API ([like this](https://github.com/avajs/ava/pull/663)) will likely be rejected without some other substantial benefit. + +*Looking to make your first ever contribution to an open-source project? Look no further! AVA may be one of the most welcoming projects and communities out there. Check out ["Making your first contribution"](https://medium.com/@vadimdemedes/making-your-first-contribution-de6576ddb190) blog post to start off the right way and make your work a part of AVA!* diff --git a/docs/common-pitfalls.md b/docs/common-pitfalls.md index 0089fa052..b467aa410 100644 --- a/docs/common-pitfalls.md +++ b/docs/common-pitfalls.md @@ -18,30 +18,50 @@ You may be using a service that only allows a limited number of concurrent conne Use the `concurrency` flag to limit the number of processes ran. For example, if your service plan allows 5 clients, you should run AVA with `concurrency=5` or less. -## Async operations +## Asynchronous operations -You may be running an async operation inside a test and wondering why it's not finishing. If your async operation uses promises, you should return the promise: +You may be running an asynchronous operation inside a test and wondering why it's not finishing. If your asynchronous operation uses promises, you should return the promise: ```js test(t => { - return fetch().then(data => { - t.is(data, 'foo'); - }); + return fetch().then(data => { + t.is(data, 'foo'); + }); }); ``` -If it uses callbacks, use [`test.cb`](https://github.com/avajs/ava#callback-support): +Better yet, use `async` / `await`: + +```js +test(async t => { + const data = await fetch(); + t.is(data, 'foo'); +}); +``` + +If you're using callbacks, use [`test.cb`](https://github.com/avajs/ava#callback-support): ```js test.cb(t => { - fetch((err, data) => { - t.is(data, 'bar'); - t.end(); - }); + fetch((err, data) => { + t.is(data, 'foo'); + t.end(); + }); }); ``` -Alternatively, promisify the callback function using something like [pify](https://github.com/sindresorhus/pify). +Alternatively, promisify the callback function using something like [`pify`](https://github.com/sindresorhus/pify): + +```js +test(async t => { + const data = await pify(fetch)(); + t.is(data, 'foo'); +}); +``` + +### Attributing uncaught exceptions to tests + +AVA [can't trace uncaught exceptions](https://github.com/avajs/ava/issues/214) back to the test that triggered them. Callback-taking functions may lead to uncaught exceptions that can then be hard to debug. Consider promisifying and using `async`/`await`, as in the above example. This should allow AVA to catch the exception and attribute it to the correct test. --- diff --git a/docs/recipes/browser-testing.md b/docs/recipes/browser-testing.md index 34afb8831..802c0ce43 100644 --- a/docs/recipes/browser-testing.md +++ b/docs/recipes/browser-testing.md @@ -7,29 +7,35 @@ An example of this is React, at least if you want to use ReactDOM.render and sim This recipe works for any library that needs a mocked browser environment. -## Install jsdom +## Install browser-env -Install [jsdom](https://github.com/tmpvar/jsdom). +Install [browser-env](https://github.com/lukechilds/browser-env). -> A JavaScript implementation of the WHATWG DOM and HTML standards, for use with node.js +> Simulates a global browser environment using jsdom. ``` -$ npm install --save-dev jsdom +$ npm install --save-dev browser-env ``` -## Setup jsdom +## Setup browser-env Create a helper file and place it in the `test/helpers` folder. This ensures AVA does not treat it as a test. `test/helpers/setup-browser-env.js`: ```js -global.document = require('jsdom').jsdom(''); -global.window = document.defaultView; -global.navigator = window.navigator; +import browserEnv from 'browser-env'; +browserEnv(); ``` -## Configure tests to use jsdom +By default, `browser-env` will add all global browser variables to the Node.js global scope, creating a full browser environment. This should have good compatibility with most front-end libraries, however, it's generally not a good idea to create lots of global variables if you don't need to. If you know exactly which browser globals you need, you can pass an array of them. + +```js +import browserEnv from 'browser-env'; +browserEnv(['window', 'document', 'navigator']); +``` + +## Configure tests to use browser-env Configure AVA to `require` the helper before every test file. @@ -47,7 +53,20 @@ Configure AVA to `require` the helper before every test file. ## Enjoy! -Write your tests and enjoy a mocked window object. +Write your tests and enjoy a mocked browser environment. + +`test/my.dom.test.js`: + +```js +import test from 'ava'; + +test('Insert to DOM', t => { + const div = document.createElement('div'); + document.body.appendChild(div); + + t.is(document.querySelector('div'), div); +}); +``` `test/my.react.test.js`: diff --git a/docs/recipes/code-coverage.md b/docs/recipes/code-coverage.md index 8ef62c82d..f5a492924 100644 --- a/docs/recipes/code-coverage.md +++ b/docs/recipes/code-coverage.md @@ -99,8 +99,6 @@ To use the Babel require hook, add `babel-core/register` to the `require` sectio } ``` -*Note*: You can also set the require hook from the command line: `ava --require=babel-core/register`. However, configuring it in `package.json` saves you from repeatedly typing that flag. - ### Putting it all together Combining the above steps, your complete `package.json` should look something like this: diff --git a/docs/recipes/watch-mode.md b/docs/recipes/watch-mode.md index 1fc2f1d0e..2f3badce8 100644 --- a/docs/recipes/watch-mode.md +++ b/docs/recipes/watch-mode.md @@ -77,7 +77,7 @@ If your tests write to disk they may trigger the watcher to rerun your tests. If AVA tracks which source files your test files depend on. If you change such a dependency only the test file that depends on it will be rerun. AVA will rerun all tests if it cannot determine which test file depends on the changed source file. -Dependency tracking works for required modules. Custom extensions and transpilers are supported, provided you loaded them using the [`--require` CLI flag] and not from inside your test file. Files accessed using the `fs` module are not tracked. +Dependency tracking works for required modules. Custom extensions and transpilers are supported, provided you [added them in your `package.json`], and not from inside your test file. Files accessed using the `fs` module are not tracked. ## Watch mode and the `.only` modifier @@ -109,7 +109,7 @@ Watch mode is relatively new and there might be some rough edges. Please [report [`chokidar`]: https://github.com/paulmillr/chokidar [Install Troubleshooting]: https://github.com/paulmillr/chokidar#install-troubleshooting [`ignore-by-default`]: https://github.com/novemberborn/ignore-by-default -[`--require` CLI flag]: https://github.com/avajs/ava#cli [`--source` CLI flag]: https://github.com/avajs/ava#cli [`.only` modifier]: https://github.com/avajs/ava#running-specific-tests [`ava` section of your `package.json`]: https://github.com/avajs/ava#configuration +[added them in your `package.json`]: https://github.com/avajs/ava#configuration diff --git a/lib/assert.js b/lib/assert.js index 2846acf9d..97227996c 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -1,7 +1,7 @@ 'use strict'; var util = require('util'); var assert = require('core-assert'); -var deepEqual = require('not-so-shallow'); +var deepEqual = require('lodash.isequal'); var observableToPromise = require('observable-to-promise'); var isObservable = require('is-observable'); var isPromise = require('is-promise'); diff --git a/lib/babel-config.js b/lib/babel-config.js index dbfddf546..1bfdafb7f 100644 --- a/lib/babel-config.js +++ b/lib/babel-config.js @@ -4,6 +4,7 @@ var chalk = require('chalk'); var figures = require('figures'); var convertSourceMap = require('convert-source-map'); var objectAssign = require('object-assign'); +var semver = require('semver'); var colors = require('./colors'); function validate(conf) { @@ -40,9 +41,13 @@ function lazy(initFn) { } var defaultPresets = lazy(function () { + var esPreset = semver.satisfies(process.version, '>=4') ? + 'babel-preset-es2015-node4' : + 'babel-preset-es2015'; + return [ require('babel-preset-stage-2'), - require('babel-preset-es2015') + require(esPreset) // eslint-disable-line import/no-dynamic-require ]; }); diff --git a/lib/caching-precompiler.js b/lib/caching-precompiler.js index 67298bb3b..f543f5dfa 100644 --- a/lib/caching-precompiler.js +++ b/lib/caching-precompiler.js @@ -76,7 +76,8 @@ CachingPrecompiler.prototype._transform = function (code, filePath, hash) { CachingPrecompiler.prototype._createTransform = function () { var salt = packageHash.sync( [require.resolve('../package.json')].concat(babelConfigHelper.pluginPackages), - JSON.stringify(this.babelConfig) + JSON.stringify(this.babelConfig), + process.version.split('.')[0] ); return cachingTransform({ diff --git a/lib/process-adapter.js b/lib/process-adapter.js index 4919fb008..1e73b591d 100644 --- a/lib/process-adapter.js +++ b/lib/process-adapter.js @@ -59,7 +59,7 @@ if (debug.enabled) { process.argv.push('--sorted'); } - require('time-require'); + require('time-require'); // eslint-disable-line import/no-unassigned-import } var sourceMapCache = Object.create(null); diff --git a/lib/reporters/mini.js b/lib/reporters/mini.js index f4d347fcb..c5d53415f 100644 --- a/lib/reporters/mini.js +++ b/lib/reporters/mini.js @@ -260,7 +260,7 @@ MiniReporter.prototype._update = function (data) { lastLine = lastLine.substring(lastLine.length - (lastLine.length % columns)); // Don't delete the last log line if it's completely empty. - if (lastLine.length) { + if (lastLine.length > 0) { ct++; } @@ -270,7 +270,7 @@ MiniReporter.prototype._update = function (data) { // Rewrite the last log line. str += lastLine; - if (str.length) { + if (str.length > 0) { this.stream.write(str); } @@ -282,7 +282,7 @@ MiniReporter.prototype._update = function (data) { var currentStatus = this.currentStatus; - if (currentStatus.length) { + if (currentStatus.length > 0) { lastLine = this.lastLineTracker.lastLine(); // We need a newline at the end of the last log line, before the status message. // However, if the last log line is the exact width of the terminal a newline is implied, diff --git a/lib/test-worker.js b/lib/test-worker.js index dbdddf3a3..2428bf6d5 100644 --- a/lib/test-worker.js +++ b/lib/test-worker.js @@ -1,6 +1,7 @@ 'use strict'; /* eslint-disable import/order */ var process = require('./process-adapter'); + var opts = process.opts; var testPath = opts.file; @@ -29,7 +30,7 @@ process.installPrecompilerHook(); var dependencies = []; process.installDependencyTracking(dependencies, testPath); -require(testPath); +require(testPath); // eslint-disable-line import/no-dynamic-require process.on('unhandledRejection', throwsHelper); diff --git a/lib/throws-helper.js b/lib/throws-helper.js index c8ff5e527..4335b5f9a 100644 --- a/lib/throws-helper.js +++ b/lib/throws-helper.js @@ -4,7 +4,7 @@ var path = require('path'); var chalk = require('chalk'); var globals = require('./globals'); -module.exports = function throwsHelper(error) { +module.exports = function (error) { if (!error || !error._avaThrowsHelperData) { return; } diff --git a/package.json b/package.json index 612210003..1d78dd2b9 100644 --- a/package.json +++ b/package.json @@ -93,15 +93,16 @@ "auto-bind": "^0.1.0", "ava-files": "^0.2.0", "ava-init": "^0.1.0", - "babel-code-frame": "^6.7.5", - "babel-core": "^6.3.21", + "babel-code-frame": "^6.16.0", + "babel-core": "^6.17.0", "babel-plugin-ava-throws-helper": "^0.1.0", "babel-plugin-detective": "^2.0.0", - "babel-plugin-espower": "^2.2.0", - "babel-plugin-transform-runtime": "^6.3.13", - "babel-preset-es2015": "^6.3.13", - "babel-preset-stage-2": "^6.3.13", - "babel-runtime": "^6.3.19", + "babel-plugin-espower": "^2.3.1", + "babel-plugin-transform-runtime": "^6.15.0", + "babel-preset-es2015": "^6.16.0", + "babel-preset-es2015-node4": "^2.1.0", + "babel-preset-stage-2": "^6.17.0", + "babel-runtime": "^6.11.6", "bluebird": "^3.0.0", "caching-transform": "^1.0.0", "chalk": "^1.0.0", @@ -131,13 +132,13 @@ "last-line-stream": "^1.0.0", "lodash.debounce": "^4.0.3", "lodash.difference": "^4.3.0", + "lodash.isequal": "^4.4.0", "loud-rejection": "^1.2.0", "matcher": "^0.1.1", "max-timeout": "^1.0.0", "md5-hex": "^1.2.0", "meow": "^3.7.0", "ms": "^0.7.1", - "not-so-shallow": "^0.1.3", "object-assign": "^4.0.1", "observable-to-promise": "^0.4.0", "option-chain": "^0.1.0", @@ -151,6 +152,7 @@ "repeating": "^2.0.0", "require-precompiled": "^0.1.0", "resolve-cwd": "^1.0.0", + "semver": "^5.3.0", "set-immediate-shim": "^1.0.1", "source-map-support": "^0.4.0", "stack-utils": "^0.4.0", @@ -173,31 +175,21 @@ "inquirer": "^1.0.2", "lolex": "^1.4.0", "mkdirp": "^0.5.1", - "nyc": "^7.1.0", + "nyc": "^8.3.0", "pify": "^2.3.0", "proxyquire": "^1.7.4", "rimraf": "^2.5.0", "signal-exit": "^3.0.0", "sinon": "^1.17.2", "source-map-fixtures": "^2.1.0", - "tap": "^6.3.0", + "tap": "^7.1.2", "touch": "^1.0.0", - "xo": "*", + "xo": "^0.16.0", "zen-observable": "^0.3.0" }, "xo": { "rules": { - "import/newline-after-import": 0 - }, - "overrides": [ - { - "files": [ - "test/**/*.js" - ], - "rules": { - "max-lines": 0 - } - } - ] + "import/newline-after-import": "off" + } } } diff --git a/profile.js b/profile.js index beadc8dc7..3cda7afef 100644 --- a/profile.js +++ b/profile.js @@ -9,7 +9,6 @@ var EventEmitter = require('events').EventEmitter; var meow = require('meow'); var Promise = require('bluebird'); var pkgConf = require('pkg-conf'); -var arrify = require('arrify'); var findCacheDir = require('find-cache-dir'); var uniqueTempDir = require('unique-temp-dir'); var CachingPrecompiler = require('./lib/caching-precompiler'); @@ -35,12 +34,10 @@ var cli = meow([ 'Options', ' --fail-fast Stop after first test failure', ' --serial, -s Run tests serially', - ' --require, -r Module to preload (Can be repeated)', '' ], { string: [ - '_', - 'require' + '_' ], boolean: [ 'fail-fast', @@ -50,7 +47,6 @@ var cli = meow([ ], default: conf, alias: { - r: 'require', s: 'serial' } }); @@ -71,7 +67,6 @@ var opts = { file: file, failFast: cli.flags.failFast, serial: cli.flags.serial, - require: arrify(cli.flags.require), tty: false, cacheDir: cacheDir, precompiled: precompiled diff --git a/readme.md b/readme.md index 9ebe07715..5e8fe2ec8 100644 --- a/readme.md +++ b/readme.md @@ -47,6 +47,7 @@ Translations: [EspaƱol](https://github.com/avajs/ava-docs/blob/master/es_ES/rea - [Enhanced assertion messages](#enhanced-assertion-messages) - [TAP reporter](#tap-reporter) - [Clean stack traces](#clean-stack-traces) +- [Automatic migration from other test runners](https://github.com/avajs/ava-codemods#migrating-to-ava) ## Test syntax @@ -140,7 +141,6 @@ $ ava --help --init Add AVA to your project --fail-fast Stop after first test failure --serial, -s Run tests serially - --require, -r Module to preload (Can be repeated) --tap, -t Generate TAP output --verbose, -v Enable verbose output --no-cache Disable the transpiler cache @@ -251,7 +251,7 @@ Test files are run from their current directory, so [`process.cwd()`](https://no ### Creating tests -To create a test you call the `test` function you imported from AVA. Provide the optional title and implementation function. The function will be called when your test is run. It's passed an [execution object](#t) as its first and only argument. By convention this argument is named `t`. +To create a test you call the `test` function you imported from AVA. Provide the optional title and implementation function. The function will be called when your test is run. It's passed an [execution object](#t) as its first argument. By convention this argument is named `t`. ```js import test from 'ava'; @@ -704,7 +704,7 @@ See AVA's [TypeScript recipe](docs/recipes/typescript.md) for a more detailed ex AVA currently only transpiles the tests you ask it to run. *It will not transpile modules you `import` from outside of the test.* This may be unexpected but there are workarounds. -If you use Babel you can use its [require hook](https://babeljs.io/docs/usage/require/) to transpile imported modules on-the-fly. Run AVA with `--require babel-register` (see [CLI](#cli)) or [configure it in your `package.json`](#configuration). +If you use Babel you can use its [require hook](https://babeljs.io/docs/usage/require/) to transpile imported modules on-the-fly. To add it, [configure it in your `package.json`](#configuration). You can also transpile your modules in a separate process and refer to the transpiled files rather than the sources from your tests. diff --git a/test/api.js b/test/api.js index c3bcb0795..69dd3e8f6 100644 --- a/test/api.js +++ b/test/api.js @@ -1031,7 +1031,7 @@ function generatePassDebugTests(execArgv, expectedInspectIndex) { t.plan(expectedInspectIndex === -1 ? 3 : 2); var api = new Api({testOnlyExecArgv: execArgv}); - return api.computeForkExecArgs(['foo.js']) + return api._computeForkExecArgs(['foo.js']) .then(function (result) { t.true(result.length === 1); if (expectedInspectIndex === -1) { diff --git a/test/assert.js b/test/assert.js index fcde1d959..3568c92c9 100644 --- a/test/assert.js +++ b/test/assert.js @@ -199,7 +199,7 @@ test('.deepEqual()', function (t) { assert.deepEqual(x, y); }); - t.doesNotThrow(function () { + t.throws(function () { function Foo(a) { this.a = a; } @@ -238,6 +238,88 @@ test('.deepEqual()', function (t) { assert.deepEqual({0: 'a', 1: 'b'}, ['a', 'b']); }); + t.throws(function () { + assert.deepEqual({a: 1}, {a: 1, b: undefined}); + }); + + t.throws(function () { + assert.deepEqual(new Date('1972-08-01'), null); + }); + + t.throws(function () { + assert.deepEqual(new Date('1972-08-01'), undefined); + }); + + t.doesNotThrow(function () { + assert.deepEqual(new Date('1972-08-01'), new Date('1972-08-01')); + }); + + t.doesNotThrow(function () { + assert.deepEqual({x: new Date('1972-08-01')}, {x: new Date('1972-08-01')}); + }); + + t.throws(function () { + assert.deepEqual(function () {}, function () {}); + }); + + t.doesNotThrow(function () { + assert.deepEqual(undefined, undefined); + assert.deepEqual({x: undefined}, {x: undefined}); + assert.deepEqual({x: [undefined]}, {x: [undefined]}); + }); + + t.doesNotThrow(function () { + assert.deepEqual(null, null); + assert.deepEqual({x: null}, {x: null}); + assert.deepEqual({x: [null]}, {x: [null]}); + }); + + t.doesNotThrow(function () { + assert.deepEqual(0, 0); + assert.deepEqual(1, 1); + assert.deepEqual(3.14, 3.14); + }); + + t.throws(function () { + assert.deepEqual(0, 1); + }); + + t.throws(function () { + assert.deepEqual(1, -1); + }); + + t.throws(function () { + assert.deepEqual(3.14, 2.72); + }); + + t.throws(function () { + assert.deepEqual({0: 'a', 1: 'b'}, ['a', 'b']); + }); + + t.doesNotThrow(function () { + assert.deepEqual( + [ + {foo: {z: 100, y: 200, x: 300}}, + 'bar', + 11, + {baz: {d: 4, a: 1, b: 2, c: 3}} + ], + [ + {foo: {x: 300, y: 200, z: 100}}, + 'bar', + 11, + {baz: {c: 3, b: 2, a: 1, d: 4}} + ] + ); + }); + + t.doesNotThrow(function () { + assert.deepEqual( + {x: {a: 1, b: 2}, y: {c: 3, d: 4}}, + {y: {d: 4, c: 3}, x: {b: 2, a: 1}} + ); + }); + // Regression test end here t.doesNotThrow(function () { diff --git a/test/cli.js b/test/cli.js index e0c138442..880116e8e 100644 --- a/test/cli.js +++ b/test/cli.js @@ -170,7 +170,7 @@ test('pkg-conf: pkg-overrides', function (t) { }); test('pkg-conf: cli takes precedence', function (t) { - execCli(['--match=foo*', '--no-serial', '--cache', '--no-fail-fast', '--require=./required.js', 'c.js'], {dirname: 'fixture/pkg-conf/precedence'}, function (err) { + execCli(['--match=foo*', '--no-serial', '--cache', '--no-fail-fast', 'c.js'], {dirname: 'fixture/pkg-conf/precedence'}, function (err) { t.ifError(err); t.end(); }); @@ -372,16 +372,13 @@ test('prefers local version of ava', function (t) { t.end(); }); -test('use current working directory if `package.json` is not found', function (t) { - var dir = uniqueTempDir({create: true}); - var testFilePath = path.join(dir, 'test.js'); +test('use current working directory if `package.json` is not found', function () { + var cwd = uniqueTempDir({create: true}); + var testFilePath = path.join(cwd, 'test.js'); var cliPath = require.resolve('../cli.js'); - var avaPath = require.resolve('../index.js'); + var avaPath = require.resolve('../'); fs.writeFileSync(testFilePath, 'import test from \'' + avaPath + '\';\ntest(t => { t.pass(); });'); - execa(process.execPath, [cliPath], {cwd: dir}).then(() => { - t.pass(); - t.end(); - }); + return execa(process.execPath, [cliPath], {cwd: cwd}); }); diff --git a/test/fixture/pkg-conf/pkg-overrides/actual.js b/test/fixture/pkg-conf/pkg-overrides/actual.js index 5320272e0..b7e33491e 100644 --- a/test/fixture/pkg-conf/pkg-overrides/actual.js +++ b/test/fixture/pkg-conf/pkg-overrides/actual.js @@ -7,7 +7,4 @@ test(t => { t.is(opts.failFast, true); t.is(opts.serial, true); t.is(opts.cacheEnabled, false); - t.deepEqual(opts.require, [ - path.join(__dirname, "required.js") - ]); }); diff --git a/test/fixture/pkg-conf/precedence/c.js b/test/fixture/pkg-conf/precedence/c.js index c0c75968f..45abfe2ea 100644 --- a/test/fixture/pkg-conf/precedence/c.js +++ b/test/fixture/pkg-conf/precedence/c.js @@ -8,7 +8,4 @@ test('foo', t => { t.is(opts.serial, false); t.is(opts.cacheEnabled, true); t.deepEqual(opts.match, ['foo*']); - t.deepEqual(opts.require, [ - path.join(__dirname, "required.js") - ]); }); diff --git a/test/runner.js b/test/runner.js index c6778e531..d960c4621 100644 --- a/test/runner.js +++ b/test/runner.js @@ -52,7 +52,7 @@ test('tests must be declared synchronously', function (t) { test('runner emits a "test" event', function (t) { var runner = new Runner(); - runner.test(function foo(a) { + runner.test('foo', function (a) { a.pass(); }); diff --git a/test/test.js b/test/test.js index 6fb41cd9f..e284ce86e 100644 --- a/test/test.js +++ b/test/test.js @@ -87,7 +87,7 @@ test('callback is required', function (t) { }); test('infer name from function', function (t) { - var result = ava(function foo(a) { + var result = ava(function foo(a) { // eslint-disable-line func-names a.pass(); }).run(); diff --git a/test/watcher.js b/test/watcher.js index a835940da..b03e9edd1 100644 --- a/test/watcher.js +++ b/test/watcher.js @@ -14,7 +14,7 @@ var setImmediate = require('../lib/globals').setImmediate; // Helper to make using beforeEach less arduous. function makeGroup(test) { - return function group(desc, fn) { + return function (desc, fn) { test(desc, function (t) { var beforeEach = function (fn) { t.beforeEach(function (done) { From 511353f54e4bbf17d7723b454ff5cf35d525015f Mon Sep 17 00:00:00 2001 From: Juan Soto Date: Sun, 23 Oct 2016 11:54:43 -0400 Subject: [PATCH 7/7] Attempt AppVeyor fix --- test/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cli.js b/test/cli.js index 880116e8e..ba758cd84 100644 --- a/test/cli.js +++ b/test/cli.js @@ -378,7 +378,7 @@ test('use current working directory if `package.json` is not found', function () var cliPath = require.resolve('../cli.js'); var avaPath = require.resolve('../'); - fs.writeFileSync(testFilePath, 'import test from \'' + avaPath + '\';\ntest(t => { t.pass(); });'); + fs.writeFileSync(testFilePath, 'import test from ' + JSON.stringify(avaPath) + ';\ntest(t => { t.pass(); });'); return execa(process.execPath, [cliPath], {cwd: cwd}); });