fava
Version:
A wannabe tiny largely-drop-in replacement for ava that works in the browser too.
102 lines (101 loc) • 4.13 kB
JavaScript
/* IMPORT */
import { execFileSync } from 'node:child_process';
import delay from 'promise-resolve-timeout';
import color from 'tiny-colors';
import readdir from 'tiny-readdir';
import Watcher from 'watcher';
import zeptomatch from 'zeptomatch';
import Env from './env.js';
import Utils from './utils.js';
/* MAIN */
const CLI = {
/* API */
execute: async (globs, options) => {
//TODO: Implement this better
//TODO: Add support for executing different test files in parallel
const filterGlobs = globs;
const filter = (targetPath) => zeptomatch(filterGlobs, targetPath);
const includeGlobs = ['**/test.{js,mjs}', '**/src/test.{js,mjs}', '**/source/test.{js,mjs}', '**/test-*.{js,mjs}', '**/*.spec.{js,mjs}', '**/*.test.{js,mjs}', '**/test/**/*.{js,mjs}', '**/tests/**/*.{js,mjs}', '**/__tests__/**/*.{js,mjs}'];
const include = (targetPath) => zeptomatch(includeGlobs, targetPath);
const ignoreGlobs = ['**/.git/**', '**/node_modules/**', '**/dist/**', '**/out/**', '**/_[!_]*', '**/fixtures.{js,mjs}', '**/__tests__/**/__helper__/**/*', '**/__tests__/**/__helpers__/**/*', '**/__tests__/**/__fixture__/**/*', '**/__tests__/**/__fixtures__/**/*', '**/test/**/helper/**/*', '**/test/**/helpers/**/*', '**/test/**/fixture/**/*', '**/test/**/fixtures/**/*', '**/tests/**/helper/**/*', '**/tests/**/helpers/**/*', '**/tests/**/fixture/**/*', '**/tests/**/fixtures/**/*'];
const ignore = (targetPath) => zeptomatch(ignoreGlobs, targetPath);
const spawn = (filePath) => {
try {
execFileSync(process.execPath, [filePath], {
stdio: ['ignore', 'inherit', 'inherit'],
env: {
...process.env,
FAVA_CLI: '1',
FAVA_FAIL_FAST: options.failFast ? '1' : '0',
FAVA_MATCH: options.match?.length ? options.match.join(',') : '',
FAVA_TIMEOUT: options.timeout ? String(options.timeout) : '0',
FAVA_VERBOSE: options.verbose ? '1' : '0'
}
});
return true;
}
catch {
return false;
}
};
const divider = (() => {
let count = 0;
return () => {
if (count) {
const width = Utils.getStdoutColumns();
const divider = color.dim('-'.repeat(width));
console.log(divider);
}
count += 1;
};
})();
const execute = (filePath) => {
divider();
const isSuccess = spawn(filePath);
if ((Env.options.failFast || options.failFast) && !isSuccess && !options.watch) {
process.exit(1);
}
};
const watch = () => {
const watcherOptions = {
native: true,
recursive: true,
ignoreInitial: false,
debounce: 500,
ignore
};
new Watcher(process.cwd(), watcherOptions, (event, targetPath) => onChange(targetPath));
};
const input = () => {
process.stdin.on('data', (data) => {
if (data.toString().trim().toLowerCase() !== 'rs')
return;
find();
});
};
const find = async () => {
const { files } = await readdir(process.cwd(), { ignore });
for (const file of files) {
onChange(file);
await delay(0);
}
};
const onChange = (targetPath) => {
if (!include(targetPath))
return;
if (globs.length && !filter(targetPath))
return;
execute(targetPath);
};
if (options.watch) {
watch();
input();
await Utils.lang.hang();
}
else {
await find();
}
}
};
/* EXPORT */
export default CLI;