Skip to content

Commit 39758c8

Browse files
committed
Support helper glob configuration
Fixes #2105.
1 parent 4530183 commit 39758c8

File tree

9 files changed

+138
-45
lines changed

9 files changed

+138
-45
lines changed

lib/api.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ class Api extends Emittery {
110110
const precompiler = await this._setupPrecompiler();
111111
let helpers = [];
112112
if (files.length === 0 || precompiler.enabled) {
113-
const found = await globs.findHelpersAndTests({cwd: this.options.resolveTestsFrom, ...apiOptions.globs});
113+
const helperPatterns = precompiler.enabled ? apiOptions.globs.helperPatterns : [];
114+
const found = await globs.findHelpersAndTests({cwd: this.options.resolveTestsFrom, ...apiOptions.globs, helperPatterns});
114115
if (files.length === 0) {
115116
({tests: files} = found);
116117
}

lib/cli.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ exports.run = async () => { // eslint-disable-line complexity
180180

181181
let globs;
182182
try {
183-
globs = normalizeGlobs(conf.files, conf.sources, extensions.all);
183+
globs = normalizeGlobs(conf.files, conf.helpers, conf.sources, extensions.all);
184184
} catch (error) {
185185
exit(error.message);
186186
}

lib/globs.js

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ const normalizePatterns = patterns => {
2828
});
2929
};
3030

31-
function normalizeGlobs(testPatterns, sourcePatterns, extensions) {
31+
function normalizeGlobs(testPatterns, helperPatterns, sourcePatterns, extensions) {
3232
if (typeof testPatterns !== 'undefined' && (!Array.isArray(testPatterns) || testPatterns.length === 0)) {
3333
throw new Error('The \'files\' configuration must be an array containing glob patterns.');
3434
}
3535

36+
if (typeof helperPatterns !== 'undefined' && (!Array.isArray(helperPatterns) || helperPatterns.length === 0)) {
37+
throw new Error('The \'helpers\' configuration must be an array containing glob patterns.');
38+
}
39+
3640
if (sourcePatterns && (!Array.isArray(sourcePatterns) || sourcePatterns.length === 0)) {
3741
throw new Error('The \'sources\' configuration must be an array containing glob patterns.');
3842
}
@@ -58,6 +62,12 @@ function normalizeGlobs(testPatterns, sourcePatterns, extensions) {
5862
testPatterns = defaultTestPatterns;
5963
}
6064

65+
if (helperPatterns) {
66+
helperPatterns = normalizePatterns(helperPatterns);
67+
} else {
68+
helperPatterns = [];
69+
}
70+
6171
const defaultSourcePatterns = [
6272
'**/*.snap',
6373
'ava.config.js',
@@ -75,11 +85,13 @@ function normalizeGlobs(testPatterns, sourcePatterns, extensions) {
7585
sourcePatterns = defaultSourcePatterns;
7686
}
7787

78-
return {extensions, testPatterns, sourcePatterns};
88+
return {extensions, testPatterns, helperPatterns, sourcePatterns};
7989
}
8090

8191
exports.normalizeGlobs = normalizeGlobs;
8292

93+
const hasExtension = (extensions, file) => extensions.includes(path.extname(file).slice(1));
94+
8395
const findFiles = async (cwd, patterns) => {
8496
const files = await globby(patterns, {
8597
absolute: true,
@@ -108,22 +120,35 @@ const findFiles = async (cwd, patterns) => {
108120
return files;
109121
};
110122

111-
async function findHelpersAndTests({cwd, extensions, testPatterns}) {
112-
const helpers = [];
123+
async function findHelpersAndTests({cwd, extensions, testPatterns, helperPatterns}) {
124+
// Search for tests concurrently with finding helpers.
125+
const findingTests = findFiles(cwd, testPatterns);
126+
127+
const uniqueHelpers = new Set();
128+
if (helperPatterns.length > 0) {
129+
for (const file of await findFiles(cwd, helperPatterns)) {
130+
if (!hasExtension(extensions, file)) {
131+
continue;
132+
}
133+
134+
uniqueHelpers.add(file);
135+
}
136+
}
137+
113138
const tests = [];
114-
for (const file of await findFiles(cwd, testPatterns)) {
115-
if (!extensions.includes(path.extname(file).slice(1))) {
139+
for (const file of await findingTests) {
140+
if (!hasExtension(extensions, file)) {
116141
continue;
117142
}
118143

119144
if (path.basename(file).startsWith('_')) {
120-
helpers.push(file);
121-
} else {
145+
uniqueHelpers.add(file);
146+
} else if (!uniqueHelpers.has(file)) { // Helpers cannot be tests.
122147
tests.push(file);
123148
}
124149
}
125150

126-
return {helpers, tests};
151+
return {helpers: [...uniqueHelpers], tests};
127152
}
128153

129154
exports.findHelpersAndTests = findHelpersAndTests;
@@ -175,9 +200,11 @@ const matches = (file, patterns) => {
175200
return micromatch.some(file, patterns, {ignore});
176201
};
177202

178-
function classify(file, {testPatterns, sourcePatterns}) {
179-
const isHelper = path.basename(file).startsWith('_');
180-
const isTest = !isHelper && matches(file, testPatterns);
203+
function classify(file, {extensions, helperPatterns, testPatterns, sourcePatterns}) {
204+
const ok = hasExtension(extensions, file);
205+
206+
const isHelper = ok && (path.basename(file).startsWith('_') || matches(file, helperPatterns));
207+
const isTest = ok && !isHelper && matches(file, testPatterns);
181208
const isSource = !isHelper && !isTest && matches(file, sourcePatterns);
182209
return {isHelper, isTest, isSource};
183210
}

test/api.js

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function apiCreator(options = {}) {
3232
options.babelConfig = babelPipeline.validate(options.babelConfig);
3333
options.concurrency = 2;
3434
options.extensions = options.extensions || {all: ['js'], enhancementsOnly: [], full: ['js']};
35-
options.globs = normalizeGlobs(options.files, options.sources, options.extensions.all);
35+
options.globs = normalizeGlobs(options.files, options.helpers, options.sources, options.extensions.all);
3636
options.projectDir = options.projectDir || ROOT_DIR;
3737
options.resolveTestsFrom = options.resolveTestsFrom || options.projectDir;
3838
const instance = new Api(options);
@@ -579,16 +579,15 @@ test('test file in node_modules is ignored', t => {
579579
});
580580
});
581581

582-
// TODO: Re-enable to test helpers patterns.
583-
// test('test file in helpers is ignored', t => {
584-
// t.plan(1);
585-
//
586-
// const api = apiCreator();
587-
// return api.run([path.join(__dirname, 'fixture/ignored-dirs/helpers/test.js')])
588-
// .then(runStatus => {
589-
// t.is(runStatus.stats.declaredTests, 0);
590-
// });
591-
// });
582+
test('test file in helpers is ignored', t => {
583+
t.plan(1);
584+
585+
const api = apiCreator({helpers: ['**/helpers/*'], projectDir: path.join(__dirname, 'fixture/ignored-dirs')});
586+
return api.run()
587+
.then(runStatus => {
588+
t.is(runStatus.stats.declaredTests, 1);
589+
});
590+
});
592591

593592
test('Node.js-style --require CLI argument', t => {
594593
const requirePath = './' + path.relative('.', path.join(__dirname, 'fixture/install-global.js')).replace(/\\/g, '/');

test/fixture/ignored-dirs/fixtures/test.js

Lines changed: 0 additions & 5 deletions
This file was deleted.

test/fixture/ignored-dirs/test.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import test from '../../..';
2+
3+
test('pass', t => t.pass());

test/globs.js

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function fixture(...args) {
1717
}
1818

1919
test('ignores relativeness in patterns', t => {
20-
const {testPatterns} = globs.normalizeGlobs(['./foo.js', '!./bar'], undefined, ['js']);
20+
const {testPatterns} = globs.normalizeGlobs(['./foo.js', '!./bar'], undefined, undefined, ['js']);
2121
t.deepEqual(testPatterns, ['foo.js', '!bar']);
2222
t.end();
2323
});
@@ -26,6 +26,7 @@ test('isTest', t => {
2626
const options = globs.normalizeGlobs(
2727
['**/foo*.js', '**/foo*/**/*.js', '!**/fixtures', '!**/helpers'],
2828
undefined,
29+
undefined,
2930
['js']
3031
);
3132

@@ -55,7 +56,7 @@ test('isTest', t => {
5556
});
5657

5758
test('isSource with defaults', t => {
58-
const options = globs.normalizeGlobs(undefined, undefined, ['js']);
59+
const options = globs.normalizeGlobs(undefined, undefined, undefined, ['js']);
5960

6061
function isSource(file) {
6162
t.true(globs.classify(file, options).isSource, `${file} should be a source`);
@@ -92,6 +93,7 @@ test('isSource with defaults', t => {
9293
test('isSource with negation negation patterns', t => {
9394
const options = globs.normalizeGlobs(
9495
['**/foo*'],
96+
undefined,
9597
['!**/bar*'],
9698
['js']
9799
);
@@ -102,6 +104,74 @@ test('isSource with negation negation patterns', t => {
102104
t.end();
103105
});
104106

107+
test('isHelper (prefixed only)', t => {
108+
const options = globs.normalizeGlobs(undefined, undefined, undefined, ['js']);
109+
110+
function isHelper(file) {
111+
t.true(globs.classify(file, options).isHelper, `${file} should be a helper`);
112+
}
113+
114+
function notHelper(file) {
115+
t.false(globs.classify(file, options).isHelper, `${file} should not be a helper`);
116+
}
117+
118+
notHelper('foo-bar.js');
119+
notHelper('foo.js');
120+
notHelper('foo/blah.js');
121+
notHelper('bar/foo.js');
122+
123+
isHelper('_foo-bar.js');
124+
isHelper('foo/_foo-bar.js');
125+
notHelper('fixtures/foo.js');
126+
notHelper('helpers/foo.js');
127+
128+
notHelper('snapshots/foo.js.snap');
129+
notHelper('snapshots/bar.js.snap');
130+
131+
notHelper('foo-bar.json');
132+
notHelper('foo-bar.coffee');
133+
134+
// These seem OK
135+
notHelper('bar.js');
136+
notHelper('bar/bar.js');
137+
notHelper('node_modules/foo.js');
138+
t.end();
139+
});
140+
141+
test('isHelper (with patterns)', t => {
142+
const options = globs.normalizeGlobs(undefined, ['**/f*.*'], undefined, ['js']);
143+
144+
function isHelper(file) {
145+
t.true(globs.classify(file, options).isHelper, `${file} should be a helper`);
146+
}
147+
148+
function notHelper(file) {
149+
t.false(globs.classify(file, options).isHelper, `${file} should not be a helper`);
150+
}
151+
152+
isHelper('foo-bar.js');
153+
isHelper('foo.js');
154+
notHelper('foo/blah.js');
155+
isHelper('bar/foo.js');
156+
157+
isHelper('_foo-bar.js');
158+
isHelper('foo/_foo-bar.js');
159+
isHelper('fixtures/foo.js');
160+
isHelper('helpers/foo.js');
161+
162+
notHelper('snapshots/foo.js.snap');
163+
notHelper('snapshots/bar.js.snap');
164+
165+
notHelper('foo-bar.json');
166+
notHelper('foo-bar.coffee');
167+
168+
// These seem OK
169+
notHelper('bar.js');
170+
notHelper('bar/bar.js');
171+
notHelper('node_modules/foo.js');
172+
t.end();
173+
});
174+
105175
test('findHelpersAndTests finds tests (just .js)', async t => {
106176
const fixtureDir = fixture('default-patterns');
107177
process.chdir(fixtureDir);
@@ -118,7 +188,7 @@ test('findHelpersAndTests finds tests (just .js)', async t => {
118188

119189
const {tests: actual} = await globs.findHelpersAndTests({
120190
cwd: fixtureDir,
121-
...globs.normalizeGlobs(['!**/fixtures/*.*', '!**/helpers/*.*'], undefined, ['js'])
191+
...globs.normalizeGlobs(['!**/fixtures/*.*', '!**/helpers/*.*'], undefined, undefined, ['js'])
122192
});
123193
actual.sort();
124194
t.deepEqual(actual, expected);
@@ -136,7 +206,7 @@ test('findHelpersAndTests finds tests (.js, .jsx)', async t => {
136206

137207
const {tests: actual} = await globs.findHelpersAndTests({
138208
cwd: fixtureDir,
139-
...globs.normalizeGlobs(['!**/fixtures/*.*', '!**/helpers/*.*'], undefined, ['js', 'jsx'])
209+
...globs.normalizeGlobs(['!**/fixtures/*.*', '!**/helpers/*.*'], undefined, undefined, ['js', 'jsx'])
140210
});
141211
actual.sort();
142212
t.deepEqual(actual, expected);
@@ -146,17 +216,16 @@ test('findHelpersAndTests finds helpers (just .js)', async t => {
146216
const fixtureDir = fixture('default-patterns');
147217
process.chdir(fixtureDir);
148218

149-
// TODO: Support pattern to match helpers directories.
150219
const expected = [
151-
// 'sub/directory/__tests__/helpers/foo.js',
220+
'sub/directory/__tests__/helpers/foo.js',
152221
'sub/directory/__tests__/_foo.js',
153-
// 'test/helpers/test.js',
222+
'test/helpers/test.js',
154223
'test/_foo-help.js'
155224
].sort().map(file => path.join(fixtureDir, file));
156225

157226
const {helpers: actual} = await globs.findHelpersAndTests({
158227
cwd: fixtureDir,
159-
...globs.normalizeGlobs(undefined, undefined, ['js'])
228+
...globs.normalizeGlobs(undefined, ['**/helpers/*'], undefined, ['js'])
160229
});
161230
actual.sort();
162231
t.deepEqual(actual, expected);
@@ -166,16 +235,15 @@ test('findHelpersAndTests finds helpers (.js and .jsx)', async t => {
166235
const fixtureDir = fixture('custom-extension');
167236
process.chdir(fixtureDir);
168237

169-
// TODO: Support pattern to match helpers directories.
170238
const expected = [
171-
'test/sub/_helper.jsx'
172-
// 'test/helpers/a.jsx',
173-
// 'test/helpers/b.js'
239+
'test/sub/_helper.jsx',
240+
'test/helpers/a.jsx',
241+
'test/helpers/b.js'
174242
].sort().map(file => path.join(fixtureDir, file));
175243

176244
const {helpers: actual} = await globs.findHelpersAndTests({
177245
cwd: fixtureDir,
178-
...globs.normalizeGlobs(undefined, undefined, ['js', 'jsx'])
246+
...globs.normalizeGlobs(undefined, ['**/helpers/*'], undefined, ['js', 'jsx'])
179247
});
180248
actual.sort();
181249
t.deepEqual(actual, expected);

test/helper/report.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ const run = (type, reporter, match = []) => {
9494
pattern = '*.ts';
9595
}
9696

97-
options.globs = normalizeGlobs(undefined, undefined, options.extensions.all);
97+
options.globs = normalizeGlobs(undefined, undefined, undefined, options.extensions.all);
9898

9999
const api = createApi(options);
100100
api.on('run', plan => reporter.startRun(plan));

test/watcher.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ group('chokidar', (beforeEach, test, group) => {
145145
Subject = proxyWatcher();
146146
});
147147

148-
const start = (specificFiles, sources) => new Subject({reporter, api, files: specificFiles || [], globs: normalizeGlobs(files, sources, ['js'])});
148+
const start = (specificFiles, sources) => new Subject({reporter, api, files: specificFiles || [], globs: normalizeGlobs(files, undefined, sources, ['js'])});
149149

150150
const emitChokidar = (event, path) => {
151151
chokidarEmitter.emit('all', event, path);

0 commit comments

Comments
 (0)