diff --git a/api.js b/api.js index 2b20af905..ec99e621b 100644 --- a/api.js +++ b/api.js @@ -23,7 +23,9 @@ function Api(options) { EventEmitter.call(this); - this.options = options || {}; + this.options = objectAssign({}, options); + this.options.cwd = this.options.cwd || process.cwd(); + this.options.resolveTestsFrom = this.options.resolveTestsFrom || this.options.cwd; this.options.match = this.options.match || []; this.options.require = (this.options.require || []).map(function (moduleId) { var ret = resolveCwd(moduleId); @@ -73,7 +75,7 @@ Api.prototype._onTimeout = function (runStatus) { Api.prototype.run = function (files, options) { var self = this; - return new AvaFiles(files) + return new AvaFiles({files: files, cwd: this.options.resolveTestsFrom}) .findTestFiles() .then(function (files) { return self._run(files, options); diff --git a/cli.js b/cli.js index d4b313c21..28197e2c6 100755 --- a/cli.js +++ b/cli.js @@ -1,6 +1,7 @@ #!/usr/bin/env node 'use strict'; +var path = require('path'); var debug = require('debug')('ava'); // Prefer the local installation of AVA. @@ -43,6 +44,8 @@ var conf = pkgConf.sync('ava', { } }); +var pkgDir = path.dirname(pkgConf.filepath(conf)); + // check for valid babel config shortcuts (can be either "default" or "inherit") var isValidShortcut = ['default', 'inherit'].indexOf(conf.babel) !== -1; @@ -136,6 +139,7 @@ var api = new Api({ explicitTitles: cli.flags.watch, match: arrify(cli.flags.match), babelConfig: conf.babel, + resolveTestsFrom: cli.input.length === 0 ? pkgDir : process.cwd(), timeout: cli.flags.timeout, concurrency: cli.flags.concurrency ? parseInt(cli.flags.concurrency, 10) : 0 }); diff --git a/lib/ava-files.js b/lib/ava-files.js index ef4938942..11f6530a8 100644 --- a/lib/ava-files.js +++ b/lib/ava-files.js @@ -25,11 +25,14 @@ function defaultIncludePatterns() { ]; } -function AvaFiles(files, sources) { +function AvaFiles(options) { if (!(this instanceof AvaFiles)) { throw new TypeError('Class constructor AvaFiles cannot be invoked without \'new\''); } + options = options || {}; + var files = options.files; + if (!files || !files.length) { files = defaultIncludePatterns(); } @@ -37,11 +40,13 @@ function AvaFiles(files, sources) { this.excludePatterns = defaultExcludePatterns(); this.files = files; - this.sources = sources || []; + this.sources = options.sources || []; + this.cwd = options.cwd || process.cwd(); } AvaFiles.prototype.findTestFiles = function () { return handlePaths(this.files, this.excludePatterns, { + cwd: this.cwd, cache: Object.create(null), statCache: Object.create(null), realpathCache: Object.create(null), @@ -222,6 +227,7 @@ function handlePaths(files, excludePatterns, globOptions) { return files .map(function (file) { + file = path.resolve(globOptions.cwd, file); if (fs.statSync(file).isDirectory()) { if (alreadySearchingParent(file)) { return null; diff --git a/lib/watcher.js b/lib/watcher.js index a2ae9569d..a12bd8f13 100644 --- a/lib/watcher.js +++ b/lib/watcher.js @@ -26,7 +26,10 @@ function rethrowAsync(err) { function Watcher(logger, api, files, sources) { this.debouncer = new Debouncer(this); - this.avaFiles = new AvaFiles(files, sources); + this.avaFiles = new AvaFiles({ + files: files, + sources: sources + }); this.isTest = this.avaFiles.makeTestMatcher(); diff --git a/test/ava-files.js b/test/ava-files.js index 4426b769c..a0b459550 100644 --- a/test/ava-files.js +++ b/test/ava-files.js @@ -25,7 +25,7 @@ test('requires new', function (t) { }); test('testMatcher', function (t) { - var avaFiles = new AvaFiles(['**/foo*']); + var avaFiles = new AvaFiles({files: ['**/foo*']}); var matcher = avaFiles.makeTestMatcher(); @@ -55,7 +55,7 @@ test('testMatcher', function (t) { }); test('sourceMatcher - defaults', function (t) { - var avaFiles = new AvaFiles(['**/foo*']); + var avaFiles = new AvaFiles({files: ['**/foo*']}); var matcher = avaFiles.makeSourceMatcher(); @@ -90,7 +90,10 @@ test('sourceMatcher - defaults', function (t) { }); test('sourceMatcher - allow matching specific node_modules directories', function (t) { - var avaFiles = new AvaFiles(['**/foo*'], ['node_modules/foo/**']); + var avaFiles = new AvaFiles({ + files: ['**/foo*'], + sources: ['node_modules/foo/**'] + }); var matcher = avaFiles.makeSourceMatcher(); @@ -100,7 +103,10 @@ test('sourceMatcher - allow matching specific node_modules directories', functio }); test('sourceMatcher - providing negation patterns', function (t) { - var avaFiles = new AvaFiles(['**/foo*'], ['!**/bar*']); + var avaFiles = new AvaFiles({ + files: ['**/foo*'], + sources: ['!**/bar*'] + }); var matcher = avaFiles.makeSourceMatcher(); @@ -111,7 +117,7 @@ test('sourceMatcher - providing negation patterns', function (t) { }); test('findFiles - does not return duplicates of the same file', function (t) { - var avaFiles = new AvaFiles(['**/ava-files/no-duplicates/**']); + var avaFiles = new AvaFiles({files: ['**/ava-files/no-duplicates/**']}); avaFiles.findTestFiles().then(function (files) { t.is(files.length, 2); @@ -119,6 +125,19 @@ test('findFiles - does not return duplicates of the same file', function (t) { }); }); +test('findFiles - honors cwd option', function (t) { + var avaFiles = new AvaFiles({ + files: ['**/test/*.js'], + cwd: path.join(__dirname, 'fixture', 'ava-files', 'cwd', 'dir-b') + }); + + avaFiles.findTestFiles().then(function (files) { + t.is(files.length, 1); + t.is(path.basename(files[0]), 'baz.js'); + t.end(); + }); +}); + test('findFiles - finds the correct files by default', function (t) { var fixtureDir = fixture('default-patterns'); process.chdir(fixtureDir); diff --git a/test/cli.js b/test/cli.js index 907f1bbc3..eb81c6e3a 100644 --- a/test/cli.js +++ b/test/cli.js @@ -176,6 +176,39 @@ test('pkg-conf: cli takes precedence', function (t) { }); }); +test('pkg-conf(resolve-dir): works as expected when run from the package.json directory', function (t) { + execCli(['--verbose'], {dirname: 'fixture/pkg-conf/resolve-dir'}, function (err, stdout, stderr) { + t.ifError(err); + t.match(stderr, /dir-a-base-1/); + t.match(stderr, /dir-a-base-2/); + t.notMatch(stderr, /dir-a-wrapper/); + t.notMatch(stdout, /dir-a-wrapper/); + t.end(); + }); +}); + +test('pkg-conf(resolve-dir): resolves tests from the package.json dir if none are specified on cli', function (t) { + execCli(['--verbose'], {dirname: 'fixture/pkg-conf/resolve-dir/dir-a-wrapper'}, function (err, stdout, stderr) { + t.ifError(err); + t.match(stderr, /dir-a-base-1/); + t.match(stderr, /dir-a-base-2/); + t.notMatch(stderr, /dir-a-wrapper/); + t.notMatch(stdout, /dir-a-wrapper/); + t.end(); + }); +}); + +test('pkg-conf(resolve-dir): resolves tests process.cwd() if globs are passed on the command line', function (t) { + execCli(['--verbose', 'dir-a/*.js'], {dirname: 'fixture/pkg-conf/resolve-dir/dir-a-wrapper'}, function (err, stdout, stderr) { + t.ifError(err); + t.match(stderr, /dir-a-wrapper-3/); + t.match(stderr, /dir-a-wrapper-4/); + t.notMatch(stderr, /dir-a-base/); + t.notMatch(stdout, /dir-a-base/); + t.end(); + }); +}); + test('watcher reruns test files when they changed', function (t) { var killed = false; diff --git a/test/fixture/ava-files/cwd/dir-a/test/bar.js b/test/fixture/ava-files/cwd/dir-a/test/bar.js new file mode 100644 index 000000000..8b1a39374 --- /dev/null +++ b/test/fixture/ava-files/cwd/dir-a/test/bar.js @@ -0,0 +1 @@ +// empty diff --git a/test/fixture/ava-files/cwd/dir-a/test/foo.js b/test/fixture/ava-files/cwd/dir-a/test/foo.js new file mode 100644 index 000000000..8b1a39374 --- /dev/null +++ b/test/fixture/ava-files/cwd/dir-a/test/foo.js @@ -0,0 +1 @@ +// empty diff --git a/test/fixture/ava-files/cwd/dir-b/test/baz.js b/test/fixture/ava-files/cwd/dir-b/test/baz.js new file mode 100644 index 000000000..8b1a39374 --- /dev/null +++ b/test/fixture/ava-files/cwd/dir-b/test/baz.js @@ -0,0 +1 @@ +// empty diff --git a/test/fixture/pkg-conf/resolve-dir/dir-a-wrapper/dir-a/dir-a-wrapper-3.js b/test/fixture/pkg-conf/resolve-dir/dir-a-wrapper/dir-a/dir-a-wrapper-3.js new file mode 100644 index 000000000..f7f3994d5 --- /dev/null +++ b/test/fixture/pkg-conf/resolve-dir/dir-a-wrapper/dir-a/dir-a-wrapper-3.js @@ -0,0 +1,5 @@ +import test from '../../../../../../'; + +test(t => { + t.pass(); +}); diff --git a/test/fixture/pkg-conf/resolve-dir/dir-a-wrapper/dir-a/dir-a-wrapper-4.js b/test/fixture/pkg-conf/resolve-dir/dir-a-wrapper/dir-a/dir-a-wrapper-4.js new file mode 100644 index 000000000..f7f3994d5 --- /dev/null +++ b/test/fixture/pkg-conf/resolve-dir/dir-a-wrapper/dir-a/dir-a-wrapper-4.js @@ -0,0 +1,5 @@ +import test from '../../../../../../'; + +test(t => { + t.pass(); +}); diff --git a/test/fixture/pkg-conf/resolve-dir/dir-a/dir-a-base-1.js b/test/fixture/pkg-conf/resolve-dir/dir-a/dir-a-base-1.js new file mode 100644 index 000000000..f5af94d7a --- /dev/null +++ b/test/fixture/pkg-conf/resolve-dir/dir-a/dir-a-base-1.js @@ -0,0 +1,5 @@ +import test from '../../../../../'; + +test(t => { + t.pass(); +}); diff --git a/test/fixture/pkg-conf/resolve-dir/dir-a/dir-a-base-2.js b/test/fixture/pkg-conf/resolve-dir/dir-a/dir-a-base-2.js new file mode 100644 index 000000000..f5af94d7a --- /dev/null +++ b/test/fixture/pkg-conf/resolve-dir/dir-a/dir-a-base-2.js @@ -0,0 +1,5 @@ +import test from '../../../../../'; + +test(t => { + t.pass(); +}); diff --git a/test/fixture/pkg-conf/resolve-dir/package.json b/test/fixture/pkg-conf/resolve-dir/package.json new file mode 100644 index 000000000..7634802f2 --- /dev/null +++ b/test/fixture/pkg-conf/resolve-dir/package.json @@ -0,0 +1,7 @@ +{ + "name": "application-name", + "version": "0.0.1", + "ava": { + "files": "dir-a/*.js" + } +} diff --git a/test/watcher.js b/test/watcher.js index 993fd2729..5c27192e8 100644 --- a/test/watcher.js +++ b/test/watcher.js @@ -553,8 +553,8 @@ group('chokidar is installed', function (beforeEach, test, group) { test('initial exclude patterns override whether something is a test file', function (t) { t.plan(2); - avaFiles = function (files, sources) { - var ret = new AvaFiles(files, sources); + avaFiles = function (options) { + var ret = new AvaFiles(options); // Note: There is no way for users to actually set exclude patterns yet. // This test just validates that internal updates to the default excludes pattern will be obeyed. ret.excludePatterns = ['!*bar*']; @@ -632,8 +632,8 @@ group('chokidar is installed', function (beforeEach, test, group) { test('exclude patterns override directory matches', function (t) { t.plan(2); - avaFiles = function (files, sources) { - var ret = new AvaFiles(files, sources); + avaFiles = function (options) { + var ret = new AvaFiles(options); // Note: There is no way for users to actually set exclude patterns yet. // This test just validates that internal updates to the default excludes pattern will be obeyed. ret.excludePatterns = ['!**/exclude/**'];