UNPKG

fava

Version:

A wannabe tiny largely-drop-in replacement for ava that works in the browser too.

125 lines (97 loc) 3.87 kB
/* 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'; import Utils from './utils'; import type {Options} from './types'; /* MAIN */ const CLI = { /* API */ execute: async ( globs: string[], options: Options ): Promise<void> => { //TODO: Implement this better //TODO: Add support for executing different test files in parallel const filterGlobs = globs; const filter = ( targetPath: string ): boolean => 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: string ): boolean => 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: string ): boolean => zeptomatch ( ignoreGlobs, targetPath ); const spawn = ( filePath: string ): boolean => { 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 (): void => { if ( count ) { const width = Utils.getStdoutColumns (); const divider = color.dim ( '-'.repeat ( width ) ); console.log ( divider ); } count += 1; }; })(); const execute = ( filePath: string ): void => { divider (); const isSuccess = spawn ( filePath ); if ( ( Env.options.failFast || options.failFast ) && !isSuccess && !options.watch ) { process.exit ( 1 ); } }; const watch = (): void => { const watcherOptions = { native: true, recursive: true, ignoreInitial: false, debounce: 500, ignore }; new Watcher ( process.cwd (), watcherOptions, ( event, targetPath ) => onChange ( targetPath ) ); }; const input = (): void => { process.stdin.on ( 'data', ( data: Buffer ): void => { if ( data.toString ().trim ().toLowerCase () !== 'rs' ) return; find (); }); }; const find = async (): Promise<void> => { const {files} = await readdir ( process.cwd (), { ignore } ); for ( const file of files ) { onChange ( file ); await delay ( 0 ); } }; const onChange = ( targetPath: string ): void => { 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;