Skip to content

Commit e1ff1d3

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

File tree

5 files changed

+231
-37
lines changed

5 files changed

+231
-37
lines changed

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

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,42 @@
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. It will consider the root of the project to be the closest folder containing a `package.json` file, and will not do anything if it can't find one.
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.
58

69
## Fail
710

811
```js
912
// File: test/foo/fixtures/bar.js
13+
// Invalid because in `fixtures` folder
1014
import test from 'ava';
1115

1216
test('foo', t => {
1317
t.pass();
1418
});
1519

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

1942
test('foo', t => {
@@ -38,4 +61,32 @@ import test from 'ava';
3861
test('foo', t => {
3962
t.pass();
4063
});
64+
65+
// File: test.js
66+
import test from 'ava';
67+
68+
test('foo', t => {
69+
t.pass();
70+
});
71+
72+
// File: lib/foo.test.js
73+
// with { "files": ["lib/**/*.test.js", "utils/**/*.test.js"] }
74+
// in either `package.json` under 'ava key' or in the rule options
75+
import test from 'ava';
76+
77+
test('foo', t => {
78+
t.pass();
79+
});
80+
```
81+
82+
## Options
83+
84+
This rule supports the following options:
85+
86+
`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.
87+
88+
You can set the options like this:
89+
90+
```js
91+
"ava/no-ignored-test-files": [2, {"files": ["lib/**/*.test.js", "utils/**/*.test.js"]}]
4192
```

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: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,55 @@
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) {
6-
if (folders.indexOf('fixtures') !== -1) {
7-
return 'fixtures';
8+
var defaultFiles = [
9+
'test.js',
10+
'test-*.js',
11+
'test/**/*.js'
12+
];
13+
14+
var excludedFolders = [
15+
'**/fixtures/**',
16+
'**/helpers/**'
17+
];
18+
19+
function isIgnored(rootDir, files, filepath) {
20+
var relativeFilePath = path.relative(rootDir, filepath);
21+
if (multimatch([relativeFilePath], excludedFolders).length !== 0) {
22+
return 'Test file is ignored because it is in `' + excludedFolders.join(' ') + '`';
823
}
924

10-
if (folders.indexOf('helpers') !== -1) {
11-
return 'helpers';
25+
if (multimatch([relativeFilePath], files).length === 0) {
26+
return 'Test file is ignored because it is not in `' + files.join(' ') + '`';
1227
}
1328

1429
return null;
1530
}
1631

17-
function isIgnored(filename) {
18-
var folders = filename.split(path.sep);
19-
var ignoredFolder = isInIgnoredFolder(folders);
20-
if (!ignoredFolder) {
21-
return null;
22-
}
23-
24-
return 'Test file is ignored because it is inside a `' + ignoredFolder + '` folder';
32+
function getPackageInfo() {
33+
var packageFilePath = pkgUp.sync();
34+
return {
35+
rootDir: packageFilePath && path.dirname(packageFilePath),
36+
files: util.getAvaConfig(packageFilePath).files
37+
};
2538
}
2639

2740
/* eslint quote-props: [2, "as-needed"] */
2841
module.exports = function (context) {
2942
var ava = createAvaRule();
43+
var packageInfo = getPackageInfo();
44+
var options = context.options[0] || {};
45+
var files = options.files || packageInfo.files || defaultFiles;
3046
var hasTestCall = false;
3147

48+
if (!packageInfo.rootDir) {
49+
// Could not find a package.json folder
50+
return {};
51+
}
52+
3253
return ava.merge({
3354
CallExpression: function (node) {
3455
if (ava.isTestFile && ava.currentTestNode === node) {
@@ -40,11 +61,20 @@ module.exports = function (context) {
4061
return;
4162
}
4263

43-
var ignoredReason = isIgnored(context.getFilename());
64+
var ignoredReason = isIgnored(packageInfo.rootDir, files, context.getFilename());
4465
if (ignoredReason) {
4566
context.report(node, ignoredReason);
4667
}
4768
hasTestCall = false;
4869
}
4970
});
5071
};
72+
73+
module.exports.schema = [{
74+
type: 'object',
75+
properties: {
76+
files: {
77+
type: 'array'
78+
}
79+
}
80+
}];

test/no-ignored-test-files.js

Lines changed: 116 additions & 21 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')
38+
},
39+
{
40+
code: header + 'foo(t => {});',
41+
filename: toPath('test/foo/fixtures/bar.js')
1942
},
2043
{
21-
code: header + 'test(t => { t.pass(); });',
22-
filename: '/foo/not-fixtures/bar.js'
44+
code: header + 'foo(t => {});',
45+
filename: toPath('test/foo/helpers/bar.js')
2346
},
2447
{
25-
code: header + 'test(t => { t.pass(); });',
26-
filename: '/foo/not-helpers/bar.js'
48+
code: code(false),
49+
filename: toPath('test/foo/fixtures/bar.js')
2750
},
2851
{
29-
code: header + 'foo(t => { t.pass(); });',
30-
filename: '/foo/bar/fixtures/baz.js'
52+
code: code(false),
53+
filename: toPath('test/foo/helpers/bar.js')
3154
},
3255
{
33-
code: header + 'foo(t => { t.pass(); });',
34-
filename: '/foo/bar/helpers/baz.js'
56+
code: code(true),
57+
filename: toPath('test.js')
3558
},
3659
{
37-
code: 'test(t => { t.pass(); });',
38-
filename: '/foo/bar/fixtures/baz.js'
60+
code: code(true),
61+
filename: toPath('test-foo.js')
3962
},
4063
{
41-
code: 'test(t => { t.pass(); });',
42-
filename: '/foo/bar/helpers/baz.js'
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',
49-
errors: [{message: 'Test file is ignored because it is inside a `fixtures` folder'}]
71+
code: code(true),
72+
filename: toPath('test/foo/fixtures/bar.js'),
73+
errors: [{message: 'Test file is ignored because it is in `**/fixtures/** **/helpers/**`'}]
74+
},
75+
{
76+
code: code(true),
77+
filename: toPath('test/foo/helpers/bar.js'),
78+
errors: [{message: 'Test file is ignored because it is in `**/fixtures/** **/helpers/**`'}]
5079
},
5180
{
52-
code: header + 'test(t => { t.pass(); });',
53-
filename: '/foo/bar/helpers/baz.js',
54-
errors: [{message: 'Test file is ignored because it is inside a `helpers` folder'}]
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 in `**/fixtures/** **/helpers/**`'}]
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 in `**/fixtures/** **/helpers/**`'}]
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)