Skip to content

Commit

Permalink
Ensure test files load correct AVA installation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
novemberborn committed Oct 18, 2016
1 parent 476c653 commit 0d29e0a
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 85 deletions.
90 changes: 5 additions & 85 deletions index.js
@@ -1,88 +1,8 @@
'use strict';
var process = require('./lib/process-adapter');
var serializeError = require('./lib/serialize-error');
var globals = require('./lib/globals');
var Runner = require('./lib/runner');
var send = process.send;

var opts = globals.options;
var runner = new Runner({
serial: opts.serial,
bail: opts.failFast,
match: opts.match
});

// note that test files have require('ava')
require('./lib/test-worker').avaRequired = true;

// if fail-fast is enabled, use this variable to detect
// that no more tests should be logged
var isFailed = false;

Error.stackTraceLimit = Infinity;

function test(props) {
if (isFailed) {
return;
}

var hasError = typeof props.error !== 'undefined';

// don't display anything if it's a passed hook
if (!hasError && props.type !== 'test') {
return;
}

if (hasError) {
props.error = serializeError(props.error);
} else {
props.error = null;
}

send('test', props);

if (hasError && opts.failFast) {
isFailed = true;
exit();
}
}

function exit() {
var stats = runner._buildStats();

send('results', {
stats: stats
});
// Ensure the same AVA install is loaded by the test file as by the test worker.
if (process.env.AVA_PATH && process.env.AVA_PATH !== __dirname) {
module.exports = require(process.env.AVA_PATH); // eslint-disable-line import/no-dynamic-require
} else {
module.exports = require('./lib/main');
}

globals.setImmediate(function () {
var hasExclusive = runner.tests.hasExclusive;
var numberOfTests = runner.tests.tests.concurrent.length + runner.tests.tests.serial.length;

if (numberOfTests === 0) {
send('no-tests', {avaRequired: true});
return;
}

send('stats', {
testCount: numberOfTests,
hasExclusive: hasExclusive
});

runner.on('test', test);

process.on('ava-run', function (options) {
runner.run(options).then(exit);
});

process.on('ava-init-exit', function () {
exit();
});
});

module.exports = runner.test;

// TypeScript imports the `default` property for
// an ES2015 default import (`import test from 'ava'`)
// See: https://github.com/Microsoft/TypeScript/issues/2242#issuecomment-83694181
module.exports.default = runner.test;
4 changes: 4 additions & 0 deletions lib/fork.js
Expand Up @@ -30,6 +30,10 @@ if (env.NODE_PATH) {
.join(path.delimiter);
}

// In case the test file imports a different AVA install, the presence of this variable allows it to require this one
// instead.
env.AVA_PATH = path.resolve(__dirname, '..');

module.exports = function (file, opts, execArgv) {
opts = objectAssign({
file: file,
Expand Down
89 changes: 89 additions & 0 deletions lib/main.js
@@ -0,0 +1,89 @@
'use strict';

var process = require('./process-adapter');
var serializeError = require('./serialize-error');
var globals = require('./globals');
var Runner = require('./runner');
var send = process.send;

var opts = globals.options;
var runner = new Runner({
serial: opts.serial,
bail: opts.failFast,
match: opts.match
});

// note that test files have require('ava')
require('./test-worker').avaRequired = true;

// if fail-fast is enabled, use this variable to detect
// that no more tests should be logged
var isFailed = false;

Error.stackTraceLimit = Infinity;

function test(props) {
if (isFailed) {
return;
}

var hasError = typeof props.error !== 'undefined';

// don't display anything if it's a passed hook
if (!hasError && props.type !== 'test') {
return;
}

if (hasError) {
props.error = serializeError(props.error);
} else {
props.error = null;
}

send('test', props);

if (hasError && opts.failFast) {
isFailed = true;
exit();
}
}

function exit() {
var stats = runner._buildStats();

send('results', {
stats: stats
});
}

globals.setImmediate(function () {
var hasExclusive = runner.tests.hasExclusive;
var numberOfTests = runner.tests.tests.concurrent.length + runner.tests.tests.serial.length;

if (numberOfTests === 0) {
send('no-tests', {avaRequired: true});
return;
}

send('stats', {
testCount: numberOfTests,
hasExclusive: hasExclusive
});

runner.on('test', test);

process.on('ava-run', function (options) {
runner.run(options).then(exit);
});

process.on('ava-init-exit', function () {
exit();
});
});

module.exports = runner.test;

// TypeScript imports the `default` property for
// an ES2015 default import (`import test from 'ava'`)
// See: https://github.com/Microsoft/TypeScript/issues/2242#issuecomment-83694181
module.exports.default = runner.test;
20 changes: 20 additions & 0 deletions test/cli.js
@@ -1,4 +1,5 @@
'use strict';
var fs = require('fs');
var path = require('path');
var childProcess = require('child_process');
var test = require('tap').test;
Expand All @@ -7,6 +8,7 @@ var getStream = require('get-stream');
var figures = require('figures');
var arrify = require('arrify');
var chalk = require('chalk');
var mkdirp = require('mkdirp');
var touch = require('touch');
var proxyquire = require('proxyquire');
var sinon = require('sinon');
Expand Down Expand Up @@ -368,3 +370,21 @@ test('prefers local version of ava', function (t) {
t.ok(debugSpy.calledWith('Using local install of AVA'));
t.end();
});

test('workers ensure test files load the same version of ava', function (t) {
var target = path.join(__dirname, 'fixture', 'ava-paths', 'target');

// Copy the index.js so the testFile imports it. It should then load the correct AVA install.
var targetInstall = path.join(target, 'node_modules', 'ava');
mkdirp.sync(targetInstall);
fs.writeFileSync(
path.join(targetInstall, 'index.js'),
fs.readFileSync(path.join(__dirname, '..', 'index.js'))
);

var testFile = path.join(target, 'test.js');
execCli([testFile], {dirname: path.join('fixture', 'ava-paths', 'cwd')}, function (err) {
t.ifError(err);
t.end();
});
});
Empty file.
5 changes: 5 additions & 0 deletions test/fixture/ava-paths/target/test.js
@@ -0,0 +1,5 @@
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable import/no-unresolved */
import test from 'ava';

test(t => t.pass());

0 comments on commit 0d29e0a

Please sign in to comment.