serverless/scripts/test-isolated.js
2019-06-04 13:04:04 +02:00

115 lines
3.5 KiB
JavaScript
Executable File

#!/usr/bin/env node
// Basic isolated tests runner
// Ensures each test file is run in distinct process and does not interfere with other test runs.
// To be used to confirm test files do not introduce and work by chance of side effects
// Temporary solution until we migrate to runner which provides that (reliably) on its own
'use strict';
process.on('unhandledRejection', err => {
throw err;
});
const globby = require('globby');
const spawn = require('child-process-ext/spawn');
const chalk = require('chalk');
const pLimit = require('p-limit');
const shouldApplyFsCleanupCheck = (() => {
const argIndex = process.argv.indexOf('--skip-fs-cleanup-check');
if (argIndex === -1) return true;
process.argv.splice(argIndex, 1);
return false;
})();
const patterns = process.argv.length <= 2 ? ['**/*.test.js'] : process.argv.slice(2);
patterns.push('!node_modules/**');
const resolveGitStatus = () =>
spawn('git', ['status', '--porcelain']).then(
({ stdoutBuffer }) => String(stdoutBuffer),
error => {
process.stdout.write(error.stdoutBuffer);
process.stderr.write(error.stderrBuffer);
throw error;
}
);
const initialGitStatusDeferred = shouldApplyFsCleanupCheck ? resolveGitStatus() : null;
const initialSetupDeferred = shouldApplyFsCleanupCheck
? initialGitStatusDeferred
: Promise.resolve();
globby(patterns).then(paths => {
if (!paths.length) {
process.stderr.write(chalk.red.bold('No test files matched\n\n'));
process.exit(1);
}
const processesCount = shouldApplyFsCleanupCheck
? 1
: Math.max(require('os').cpus().length - 1, 1);
const isMultiProcessRun = processesCount > 1;
const { ongoing, cliFooter } = (() => {
if (!isMultiProcessRun) return {};
return { ongoing: new Set(), cliFooter: require('cli-progress-footer')() };
})();
const run = path => {
if (isMultiProcessRun) {
ongoing.add(path);
cliFooter.updateProgress(Array.from(ongoing));
}
const onFinally = (() => {
if (isMultiProcessRun) {
return ({ stdoutBuffer, stderrBuffer }) => {
ongoing.delete(path);
cliFooter.updateProgress(Array.from(ongoing));
process.stdout.write(stdoutBuffer);
process.stderr.write(stderrBuffer);
return Promise.resolve();
};
}
if (!shouldApplyFsCleanupCheck) return () => Promise.resolve();
return () =>
Promise.all([initialGitStatusDeferred, resolveGitStatus()]).then(
([initialStatus, currentStatus]) => {
if (initialStatus !== currentStatus) {
process.stderr.write(
chalk.red.bold(`${path} didn't clean created temporary files\n\n`)
);
process.exit(1);
}
}
);
})();
return spawn('npx', ['mocha', path], {
stdio: isMultiProcessRun ? null : 'inherit',
env: {
APPDATA: process.env.APPDATA,
FORCE_COLOR: '1',
HOME: process.env.HOME,
PATH: process.env.PATH,
TMPDIR: process.env.TMPDIR,
USERPROFILE: process.env.USERPROFILE,
},
}).then(onFinally, error => {
if (isMultiProcessRun) ongoing.clear();
return onFinally(error).then(() => {
process.stderr.write(chalk.red.bold(`${path} failed\n\n`));
if (error.code <= 2) process.exit(error.code);
throw error;
});
});
};
const limit = pLimit(processesCount);
return initialSetupDeferred.then(() => Promise.all(paths.map(path => limit(() => run(path)))));
});