@tapjs/run
Version:
Command-line interface for the node-tap runner
122 lines • 4.65 kB
JavaScript
/**
* This provides the functionality that is shared between `tap run`
* and `tap replay`, to figure out what tests need to be run, create
* the various subtests for each, and pipe the whole thing through an
* appropriate reporter.
*
* When called by `run`, the execution method is `t.spawn`. When called
* by `replay`, the file is mapped to its appropriate saved results file in
* `.tap/test-results`, and `t.tapFile()` is called.
*
* @module
*/
// run the provided tests
import { plugin as AfterPlugin } from '@tapjs/after';
import { tap } from '@tapjs/core';
import { resolve } from 'path';
import { handleReporter } from './handle-reporter.js';
import { list } from './list.js';
import { values } from './main-config.js';
import { outputFile } from './output-file.js';
import { proxyFatalSignals } from './proxy-fatal-signals.js';
import { report } from './report.js';
import { readSave, writeSave } from './save-list.js';
export const executeTestSuite = async (args, config,
/**
* called with the tap object, so any additional plugins can be applied or
* other modifications if needed.
*/
applyPlugins,
/**
* called before running the test files, but only if we have files to run
*/
setup,
/**
* called with the mutated tap, and each test file path, relative to
* config.projectRoot
*/
execute) => {
let base = tap({
// special runner context so plugins can behave differently.
// currently, this is only used by @tapjs/filter
context: Symbol.for('tap.isRunner'),
});
if (!base.pluginLoaded(AfterPlugin)) {
base = base.applyPlugin(AfterPlugin);
}
const t = applyPlugins(base);
// we don't want to time out the runner, just the subtests
t.setTimeout(0);
// proxy all fatal signals to child processes, so that we can report
// their status properly.
proxyFatalSignals(t);
const files = await list(args, config);
if (!files.length) {
// see if we WOULD have gotten files, if not for the fact that they
// are all unchanged.
const unpruned = config.get('changed') ? await list(args, config, true) : [];
if (args.length && !unpruned.length) {
console.error('No valid test files found matching ' +
args.map(a => JSON.stringify(a)).join(' '));
if (args.length === 2 &&
args[0] === 'edit' &&
args[1] === 'config') {
console.error(`(Did you mean 'tap config edit'?)`);
}
t.debug('runner exit=1 no files found, args');
process.exitCode = 1;
}
else if (unpruned.length) {
console.log('No new tests to run');
}
else {
t.debug('runner exit=1 no files found, no args');
console.error('No test files found');
process.exitCode = 1;
}
return;
}
// jobs has a default of os.availableParallelism or 1
/* c8 ignore start */
if (typeof values.jobs !== 'number') {
throw new Error('no jobs option provided');
}
/* c8 ignore stop */
await setup(t);
const hasReporter = await handleReporter(t, config);
t.buffered = false;
const readSaveList = await readSave(config);
const saveList = config.get('save') && !readSaveList.length ? files : readSaveList;
// after() can run in the same tick that the test promise resolves,
// so we have to wait to write the save file until they're all done.
// only an issue with more than one subtests that all run in parallel,
// and end very quickly.
const testPromises = [];
if (config.get('save')) {
t.after(async () => {
await Promise.all(testPromises);
await writeSave(config, saveList);
});
}
t.jobs = Math.max(1, Math.min(values.jobs, files.length));
const stdinOnly = files.length === 1 &&
(files[0] === '-' || files[0] === '/dev/stdin');
if (!stdinOnly) {
t.teardown(() => report([], config));
t.plan(files.length);
}
outputFile(t, config, hasReporter);
const sorted = files.sort((a, b) => a.localeCompare(b, 'en'));
for (const f of sorted) {
const file = f === '-' || f === '/dev/stdin' ?
'/dev/stdin'
: resolve(config.projectRoot, f);
const p = execute(t, file, sorted, hasReporter);
testPromises.push(!saveList.length ? p : (p.then((results) => {
if (results?.ok && saveList.includes(f))
saveList.splice(saveList.indexOf(f), 1);
})));
}
return t.promise();
};
//# sourceMappingURL=execute-test-suite.js.map