Skip to content

Commit b25192e

Browse files
committed
no-ignored-test-files add files matching
1 parent a547b54 commit b25192e

File tree

5 files changed

+240
-32
lines changed

5 files changed

+240
-32
lines changed

docs/rules/no-ignored-test-files.md

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,44 @@
11
# Ensure no tests are written in ignored files
22

3-
When searching for tests, AVA ignores files contained in folders named `fixtures` or `helpers`.
3+
When searching for tests, AVA ignores files contained in folders named `fixtures` or `helpers`. By default, it will search in `test.js test-*.js test/**/*.js`, which you can override by specifying a path when launching AVA or in the [AVA configuration in the `package.json` file] ](https://github.com/sindresorhus/ava#configuration).
44

5+
This rule will verify that files which create tests are in the searched files and not in ignored folders.
6+
7+
Note that this rule will not be able to warn correctly if you use AVA by specifying the files in the command line ( `ava "lib/**/*.test.js"` ). Prefer configuring AVA as described in the link above.
8+
9+
Also note that this rule will not do anything if you don't have a `package.json` file in your project.
510

611
## Fail
712

813
```js
914
// File: test/foo/fixtures/bar.js
15+
// Invalid because in `fixtures` folder
1016
import test from 'ava';
1117

1218
test('foo', t => {
1319
t.pass();
1420
});
1521

1622
// File: test/foo/helpers/bar.js
23+
// Invalid because in `helpers` folder
24+
import test from 'ava';
25+
26+
test('foo', t => {
27+
t.pass();
28+
});
29+
30+
// File: lib/foo.test.js
31+
// Invalid because not in the searched files
32+
import test from 'ava';
33+
34+
test('foo', t => {
35+
t.pass();
36+
});
37+
38+
// File: test.js
39+
// with { "files": ["lib/**/*.test.js", "utils/**/*.test.js"] }
40+
// in either `package.json` under 'ava key' or in the rule options
41+
// Invalid because not in the searched files
1742
import test from 'ava';
1843

1944
test('foo', t => {
@@ -38,4 +63,32 @@ import test from 'ava';
3863
test('foo', t => {
3964
t.pass();
4065
});
66+
67+
// File: test.js
68+
import test from 'ava';
69+
70+
test('foo', t => {
71+
t.pass();
72+
});
73+
74+
// File: lib/foo.test.js
75+
// with { "files": ["lib/**/*.test.js", "utils/**/*.test.js"] }
76+
// in either `package.json` under 'ava key' or in the rule options
77+
import test from 'ava';
78+
79+
test('foo', t => {
80+
t.pass();
81+
});
82+
```
83+
84+
## Options
85+
86+
This rule supports the following options:
87+
88+
`files`: An array of strings representing the files glob that AVA will use to find test files. Overrides the default and the configuration found in the `package.json` file.
89+
90+
You can set the options like this:
91+
92+
```js
93+
"ava/no-ignored-test-files": [2, {"files": ["lib/**/*.test.js", "utils/**/*.test.js"]}]
4194
```

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@
6363
"dependencies": {
6464
"deep-strict-equal": "^0.1.0",
6565
"espurify": "^1.5.0",
66-
"object-assign": "^4.0.1"
66+
"multimatch": "^2.1.0",
67+
"object-assign": "^4.0.1",
68+
"pkg-up": "^1.0.0"
6769
},
6870
"devDependencies": {
6971
"ava": "*",

rules/no-ignored-test-files.js

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,67 @@
11
'use strict';
22
var path = require('path');
3+
var pkgUp = require('pkg-up');
4+
var multimatch = require('multimatch');
5+
var util = require('../util');
36
var createAvaRule = require('../create-ava-rule');
47

5-
function isInIgnoredFolder(folders) {
8+
var defaultFiles = [
9+
'test.js',
10+
'test-*.js',
11+
'test/**/*.js'
12+
];
13+
14+
function isInIgnoredFolder(relativeFilePath) {
15+
var folders = relativeFilePath.split(path.sep);
16+
var ignoredFolder;
617
if (folders.indexOf('fixtures') !== -1) {
7-
return 'fixtures';
18+
ignoredFolder = 'fixtures';
19+
} else if (folders.indexOf('helpers') !== -1) {
20+
ignoredFolder = 'helpers';
821
}
922

10-
if (folders.indexOf('helpers') !== -1) {
11-
return 'helpers';
23+
if (ignoredFolder) {
24+
return 'Test file is ignored because it is inside a `' + ignoredFolder + '` folder';
1225
}
1326

1427
return null;
1528
}
1629

17-
function isIgnored(filename) {
18-
var folders = filename.split(path.sep);
19-
var ignoredFolder = isInIgnoredFolder(folders);
20-
if (!ignoredFolder) {
21-
return null;
30+
function isNotInGlob(files, relativeFilePath) {
31+
if (multimatch([relativeFilePath], files).length === 0) {
32+
return 'Test file is ignored because it is not in `' + files.join(' ') + '`';
2233
}
2334

24-
return 'Test file is ignored because it is inside a `' + ignoredFolder + '` folder';
35+
return null;
36+
}
37+
38+
function isIgnored(rootDir, files, filepath) {
39+
var relativeFilePath = path.relative(rootDir, filepath);
40+
return isInIgnoredFolder(relativeFilePath) ||
41+
isNotInGlob(files, relativeFilePath);
42+
}
43+
44+
function getPackageInfo() {
45+
var packageFilePath = pkgUp.sync();
46+
return {
47+
rootDir: packageFilePath && path.dirname(packageFilePath),
48+
files: util.getAvaConfig(packageFilePath).files
49+
};
2550
}
2651

2752
/* eslint quote-props: [2, "as-needed"] */
2853
module.exports = function (context) {
2954
var ava = createAvaRule();
55+
var packageInfo = getPackageInfo();
56+
var options = context.options[0] || {};
57+
var files = options.files || packageInfo.files || defaultFiles;
3058
var hasTestCall = false;
3159

60+
if (!packageInfo.rootDir) {
61+
// Could not find a package.json folder
62+
return {};
63+
}
64+
3265
return ava.merge({
3366
CallExpression: function (node) {
3467
if (ava.isTestFile && ava.currentTestNode === node) {
@@ -40,11 +73,20 @@ module.exports = function (context) {
4073
return;
4174
}
4275

43-
var ignoredReason = isIgnored(context.getFilename());
76+
var ignoredReason = isIgnored(packageInfo.rootDir, files, context.getFilename());
4477
if (ignoredReason) {
4578
context.report(node, ignoredReason);
4679
}
4780
hasTestCall = false;
4881
}
4982
});
5083
};
84+
85+
module.exports.schema = [{
86+
type: 'object',
87+
properties: {
88+
files: {
89+
type: 'array'
90+
}
91+
}
92+
}];

test/no-ignored-test-files.js

Lines changed: 114 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import path from 'path';
12
import test from 'ava';
23
import {RuleTester} from 'eslint';
4+
import util from '../util';
35
import rule from '../rules/no-ignored-test-files';
46

57
const ruleTester = new RuleTester({
@@ -9,50 +11,143 @@ const ruleTester = new RuleTester({
911
});
1012

1113
const header = `const test = require('ava');\n`;
14+
const rootDir = path.dirname(process.cwd());
1215

13-
test(() => {
16+
function toPath(subPath) {
17+
return path.join(rootDir, subPath);
18+
}
19+
20+
function code(hasHeader) {
21+
return (hasHeader ? header : '') + 'test(t => { t.pass(); });';
22+
}
23+
24+
test('without AVA config in package.json', () => {
1425
ruleTester.run('no-ignored-test-files', rule, {
1526
valid: [
1627
{
17-
code: header + 'test(t => { t.pass(); });',
18-
filename: '/foo/bar/baz.js'
28+
code: code(true),
29+
filename: toPath('test/foo/bar.js')
30+
},
31+
{
32+
code: code(true),
33+
filename: toPath('test/foo/not-fixtures/bar.js')
34+
},
35+
{
36+
code: code(true),
37+
filename: toPath('test/foo/not-helpers/bar.js')
1938
},
2039
{
21-
code: header + 'test(t => { t.pass(); });',
22-
filename: '/foo/not-fixtures/bar.js'
40+
code: header + 'foo(t => {});',
41+
filename: toPath('test/foo/fixtures/bar.js')
2342
},
2443
{
25-
code: header + 'test(t => { t.pass(); });',
26-
filename: '/foo/not-helpers/bar.js'
44+
code: header + 'foo(t => {});',
45+
filename: toPath('test/foo/helpers/bar.js')
2746
},
2847
{
29-
code: header + 'foo(t => { t.pass(); });',
30-
filename: '/foo/bar/fixtures/baz.js'
48+
code: code(false),
49+
filename: toPath('test/foo/fixtures/bar.js')
3150
},
3251
{
33-
code: header + 'foo(t => { t.pass(); });',
34-
filename: '/foo/bar/helpers/baz.js'
52+
code: code(false),
53+
filename: toPath('test/foo/helpers/bar.js')
3554
},
3655
{
37-
code: 'test(t => { t.pass(); });',
38-
filename: '/foo/bar/fixtures/baz.js'
56+
code: code(true),
57+
filename: toPath('test.js')
3958
},
4059
{
41-
code: 'test(t => { t.pass(); });',
42-
filename: '/foo/bar/helpers/baz.js'
60+
code: code(true),
61+
filename: toPath('test-foo.js')
62+
},
63+
{
64+
code: code(true),
65+
filename: toPath('lib/foo.test.js'),
66+
options: [{files: ['lib/**/*.test.js']}]
4367
}
4468
],
4569
invalid: [
4670
{
47-
code: header + 'test(t => { t.pass(); });',
48-
filename: '/foo/bar/fixtures/baz.js',
71+
code: code(true),
72+
filename: toPath('test/foo/fixtures/bar.js'),
4973
errors: [{message: 'Test file is ignored because it is inside a `fixtures` folder'}]
5074
},
5175
{
52-
code: header + 'test(t => { t.pass(); });',
53-
filename: '/foo/bar/helpers/baz.js',
76+
code: code(true),
77+
filename: toPath('test/foo/helpers/bar.js'),
5478
errors: [{message: 'Test file is ignored because it is inside a `helpers` folder'}]
79+
},
80+
{
81+
code: code(true),
82+
filename: toPath('lib/foo.test.js'),
83+
errors: [{message: 'Test file is ignored because it is not in `test.js test-*.js test/**/*.js`'}]
84+
},
85+
{
86+
code: code(true),
87+
filename: toPath('test/foo/bar.js'),
88+
options: [{files: ['lib/**/*.test.js']}],
89+
errors: [{message: 'Test file is ignored because it is not in `lib/**/*.test.js`'}]
90+
},
91+
{
92+
code: code(true),
93+
filename: toPath('lib/foo.not-test.js'),
94+
options: [{files: ['lib/**/*.test.js']}],
95+
errors: [{message: 'Test file is ignored because it is not in `lib/**/*.test.js`'}]
5596
}
5697
]
5798
});
5899
});
100+
101+
test('with AVA config in package.json', () => {
102+
const oldGetAvaConfig = util.getAvaConfig;
103+
104+
util.getAvaConfig = function mockGetAvaConfig() {
105+
return {
106+
files: ['lib/**/*.test.js']
107+
};
108+
};
109+
110+
ruleTester.run('no-ignored-test-files', rule, {
111+
valid: [
112+
{
113+
code: code(true),
114+
filename: toPath('lib/foo.test.js')
115+
},
116+
{
117+
code: code(true),
118+
filename: toPath('bar/foo.test.js'),
119+
options: [{files: ['bar/**/*.test.js']}]
120+
}
121+
],
122+
invalid: [
123+
{
124+
code: code(true),
125+
filename: toPath('lib/foo/fixtures/bar.test.js'),
126+
errors: [{message: 'Test file is ignored because it is inside a `fixtures` folder'}]
127+
},
128+
{
129+
code: code(true),
130+
filename: toPath('lib/foo/helpers/bar.test.js'),
131+
errors: [{message: 'Test file is ignored because it is inside a `helpers` folder'}]
132+
},
133+
{
134+
code: code(true),
135+
filename: toPath('test.js'),
136+
errors: [{message: 'Test file is ignored because it is not in `lib/**/*.test.js`'}]
137+
},
138+
{
139+
code: code(true),
140+
filename: toPath('bar/foo.test.js'),
141+
errors: [{message: 'Test file is ignored because it is not in `lib/**/*.test.js`'}]
142+
},
143+
{
144+
code: code(true),
145+
filename: toPath('lib/foo.test.js'),
146+
options: [{files: ['bar/**/*.test.js']}],
147+
errors: [{message: 'Test file is ignored because it is not in `bar/**/*.test.js`'}]
148+
}
149+
]
150+
});
151+
152+
util.getAvaConfig = oldGetAvaConfig;
153+
});

util.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
'use strict';
22

3+
var fs = require('fs');
4+
35
exports.nameOfRootObject = function (node) {
46
if (node.object.type === 'MemberExpression') {
57
return exports.nameOfRootObject(node.object);
68
}
79

810
return node.object.name;
911
};
12+
13+
exports.getAvaConfig = function (filepath) {
14+
var defaultResult = {};
15+
if (!filepath) {
16+
return defaultResult;
17+
}
18+
19+
try {
20+
var packageContent = JSON.parse(fs.readFileSync(filepath, 'utf8'));
21+
return packageContent && packageContent.ava || defaultResult;
22+
} catch (e) {
23+
return defaultResult;
24+
}
25+
};

0 commit comments

Comments
 (0)