Skip to content

Commit 3ea2ba1

Browse files
novemberbornsindresorhus
authored andcommitted
Ensure test files load correct AVA installation (#1085)
The main process sets the AVA_PATH environment variable to the absolute path of the index.js file. Workers are loaded with this variable present. When test files require the AVA module (assuming it's a version containing this commit of course), the index.js file redirects to the one used by the worker if necessary by comparing AVA_PATH. The redirect required most of index.js to be moved into a separate module (lib/main.js). Fixes #643.
1 parent 476c653 commit 3ea2ba1

File tree

6 files changed

+123
-85
lines changed

6 files changed

+123
-85
lines changed

index.js

Lines changed: 5 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,8 @@
11
'use strict';
2-
var process = require('./lib/process-adapter');
3-
var serializeError = require('./lib/serialize-error');
4-
var globals = require('./lib/globals');
5-
var Runner = require('./lib/runner');
6-
var send = process.send;
72

8-
var opts = globals.options;
9-
var runner = new Runner({
10-
serial: opts.serial,
11-
bail: opts.failFast,
12-
match: opts.match
13-
});
14-
15-
// note that test files have require('ava')
16-
require('./lib/test-worker').avaRequired = true;
17-
18-
// if fail-fast is enabled, use this variable to detect
19-
// that no more tests should be logged
20-
var isFailed = false;
21-
22-
Error.stackTraceLimit = Infinity;
23-
24-
function test(props) {
25-
if (isFailed) {
26-
return;
27-
}
28-
29-
var hasError = typeof props.error !== 'undefined';
30-
31-
// don't display anything if it's a passed hook
32-
if (!hasError && props.type !== 'test') {
33-
return;
34-
}
35-
36-
if (hasError) {
37-
props.error = serializeError(props.error);
38-
} else {
39-
props.error = null;
40-
}
41-
42-
send('test', props);
43-
44-
if (hasError && opts.failFast) {
45-
isFailed = true;
46-
exit();
47-
}
48-
}
49-
50-
function exit() {
51-
var stats = runner._buildStats();
52-
53-
send('results', {
54-
stats: stats
55-
});
3+
// Ensure the same AVA install is loaded by the test file as by the test worker.
4+
if (process.env.AVA_PATH && process.env.AVA_PATH !== __dirname) {
5+
module.exports = require(process.env.AVA_PATH); // eslint-disable-line import/no-dynamic-require
6+
} else {
7+
module.exports = require('./lib/main');
568
}
57-
58-
globals.setImmediate(function () {
59-
var hasExclusive = runner.tests.hasExclusive;
60-
var numberOfTests = runner.tests.tests.concurrent.length + runner.tests.tests.serial.length;
61-
62-
if (numberOfTests === 0) {
63-
send('no-tests', {avaRequired: true});
64-
return;
65-
}
66-
67-
send('stats', {
68-
testCount: numberOfTests,
69-
hasExclusive: hasExclusive
70-
});
71-
72-
runner.on('test', test);
73-
74-
process.on('ava-run', function (options) {
75-
runner.run(options).then(exit);
76-
});
77-
78-
process.on('ava-init-exit', function () {
79-
exit();
80-
});
81-
});
82-
83-
module.exports = runner.test;
84-
85-
// TypeScript imports the `default` property for
86-
// an ES2015 default import (`import test from 'ava'`)
87-
// See: https://github.com/Microsoft/TypeScript/issues/2242#issuecomment-83694181
88-
module.exports.default = runner.test;

lib/fork.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ if (env.NODE_PATH) {
3030
.join(path.delimiter);
3131
}
3232

33+
// In case the test file imports a different AVA install, the presence of this variable allows it to require this one
34+
// instead.
35+
env.AVA_PATH = path.resolve(__dirname, '..');
36+
3337
module.exports = function (file, opts, execArgv) {
3438
opts = objectAssign({
3539
file: file,

lib/main.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
'use strict';
2+
3+
var process = require('./process-adapter');
4+
var serializeError = require('./serialize-error');
5+
var globals = require('./globals');
6+
var Runner = require('./runner');
7+
var send = process.send;
8+
9+
var opts = globals.options;
10+
var runner = new Runner({
11+
serial: opts.serial,
12+
bail: opts.failFast,
13+
match: opts.match
14+
});
15+
16+
// note that test files have require('ava')
17+
require('./test-worker').avaRequired = true;
18+
19+
// if fail-fast is enabled, use this variable to detect
20+
// that no more tests should be logged
21+
var isFailed = false;
22+
23+
Error.stackTraceLimit = Infinity;
24+
25+
function test(props) {
26+
if (isFailed) {
27+
return;
28+
}
29+
30+
var hasError = typeof props.error !== 'undefined';
31+
32+
// don't display anything if it's a passed hook
33+
if (!hasError && props.type !== 'test') {
34+
return;
35+
}
36+
37+
if (hasError) {
38+
props.error = serializeError(props.error);
39+
} else {
40+
props.error = null;
41+
}
42+
43+
send('test', props);
44+
45+
if (hasError && opts.failFast) {
46+
isFailed = true;
47+
exit();
48+
}
49+
}
50+
51+
function exit() {
52+
var stats = runner._buildStats();
53+
54+
send('results', {
55+
stats: stats
56+
});
57+
}
58+
59+
globals.setImmediate(function () {
60+
var hasExclusive = runner.tests.hasExclusive;
61+
var numberOfTests = runner.tests.tests.concurrent.length + runner.tests.tests.serial.length;
62+
63+
if (numberOfTests === 0) {
64+
send('no-tests', {avaRequired: true});
65+
return;
66+
}
67+
68+
send('stats', {
69+
testCount: numberOfTests,
70+
hasExclusive: hasExclusive
71+
});
72+
73+
runner.on('test', test);
74+
75+
process.on('ava-run', function (options) {
76+
runner.run(options).then(exit);
77+
});
78+
79+
process.on('ava-init-exit', function () {
80+
exit();
81+
});
82+
});
83+
84+
module.exports = runner.test;
85+
86+
// TypeScript imports the `default` property for
87+
// an ES2015 default import (`import test from 'ava'`)
88+
// See: https://github.com/Microsoft/TypeScript/issues/2242#issuecomment-83694181
89+
module.exports.default = runner.test;

test/cli.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
'use strict';
2+
var fs = require('fs');
23
var path = require('path');
34
var childProcess = require('child_process');
45
var test = require('tap').test;
@@ -7,6 +8,7 @@ var getStream = require('get-stream');
78
var figures = require('figures');
89
var arrify = require('arrify');
910
var chalk = require('chalk');
11+
var mkdirp = require('mkdirp');
1012
var touch = require('touch');
1113
var proxyquire = require('proxyquire');
1214
var sinon = require('sinon');
@@ -368,3 +370,21 @@ test('prefers local version of ava', function (t) {
368370
t.ok(debugSpy.calledWith('Using local install of AVA'));
369371
t.end();
370372
});
373+
374+
test('workers ensure test files load the same version of ava', function (t) {
375+
var target = path.join(__dirname, 'fixture', 'ava-paths', 'target');
376+
377+
// Copy the index.js so the testFile imports it. It should then load the correct AVA install.
378+
var targetInstall = path.join(target, 'node_modules', 'ava');
379+
mkdirp.sync(targetInstall);
380+
fs.writeFileSync(
381+
path.join(targetInstall, 'index.js'),
382+
fs.readFileSync(path.join(__dirname, '..', 'index.js'))
383+
);
384+
385+
var testFile = path.join(target, 'test.js');
386+
execCli([testFile], {dirname: path.join('fixture', 'ava-paths', 'cwd')}, function (err) {
387+
t.ifError(err);
388+
t.end();
389+
});
390+
});

test/fixture/ava-paths/cwd/.gitkeep

Whitespace-only changes.

test/fixture/ava-paths/target/test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* eslint-disable import/no-extraneous-dependencies */
2+
/* eslint-disable import/no-unresolved */
3+
import test from 'ava';
4+
5+
test(t => t.pass());

0 commit comments

Comments
 (0)