UNPKG

fava

Version:

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

164 lines (163 loc) 6.25 kB
/* IMPORT */ import color from 'tiny-colors'; import zeptomatch from 'zeptomatch'; import { ROOT_DESCRIBER_ID } from './constants.js'; import Describer from './describer.js'; import Env from './env.js'; import Factory from './factory.js'; import Flags from './flags.js'; import Utils from './utils.js'; /* MAIN */ class Suiter { /* CONSTRUCTOR */ constructor(title) { this.executed = false; this.scheduled = false; /* REGISTRY API */ this.registerDescriber = (describer, implementation) => { const prev = this.current; this.current.describers.push(describer); this.current = describer; this.stack.push(describer); implementation(Factory.makeTest()); this.stack.pop(); this.current = prev; }; this.registerTester = (tester) => { this.current.testers.push(tester); }; /* EXECUTION API */ this.execute = async () => { this.executed = true; await this.propagateFlags(); await this.propagateFlagOnly(); await this.propagateMatch(); await this.propagatePrefixes(); await this.root.execute(); await this.summary(); await this.propagateExitCode(); }; this.propagateExitCode = async () => { if (!Env.is.cli) return; let isSuccess = true; await this.root.visit({ onTester: tester => { if (tester.passed !== 0) return; isSuccess = false; } }); const exitCode = (isSuccess ? 0 : 1); process.exitCode = exitCode; }; this.propagateFlags = async () => { const propagate = (source, target) => { var _a; const flags = Utils.lang.keys(source.flags); for (const flag of flags) { (_a = target.flags)[flag] || (_a[flag] = source.flags[flag]); } }; const visit = (root) => { return root.visit({ onDescriber: describer => { propagate(root, describer); return visit(describer); }, onTester: tester => { propagate(root, tester); } }); }; await visit(this.root); }; this.propagateFlagOnly = async () => { let hasOnly = false; await this.root.visit({ onTester: tester => { if (!tester.flags.only) return; hasOnly = true; } }); if (!hasOnly) return; await this.root.visit({ onTester: tester => { if (tester.flags.only) return; tester.flags.skip = true; } }); }; this.propagateMatch = async () => { if (!Env.options.match.length) return; await this.root.visit({ onTester: tester => { if (tester.flags.skip) return; if (zeptomatch(Env.options.match, tester.title)) return; tester.flags.skip = true; } }); }; this.propagatePrefixes = async () => { const visit = (root, prefixes) => { return root.visit({ recursive: false, onDescriber: describer => { return visit(describer, [...prefixes, describer.title]); }, onTester: tester => { tester.prefixes = prefixes; } }); }; await visit(this.root, []); }; this.schedule = () => { if (this.scheduled) return; this.scheduled = true; setTimeout(this.execute, 0); }; this.summary = async () => { const assertions = { passed: 0, failed: 0, total: 0 }; const tests = { passed: 0, failed: 0, skipped: 0, total: 0 }; await this.root.visit({ onTester: tester => { assertions.passed += tester.stats.passed; assertions.failed += tester.stats.failed; assertions.total += tester.stats.total; tests.passed += (tester.passed === 1 ? 1 : 0); tests.failed += (tester.passed === 0 ? 1 : 0); tests.skipped += (tester.passed === -1 ? 1 : 0); tests.total += 1; } }); if (Env.options.verbose || tests.failed) { console.log(); } if (Env.is.cli) { const path = await import('node:path'); const filePath = process.argv[1]; const folderRe = /^(.*)([\\\/])(test|tests|__tests__)(\1)/; const testName = folderRe.test(filePath) ? filePath.replace(folderRe, '') : path.basename(filePath); console.log(`${color.cyan('ℹ')} File: ${testName}`); } if (Env.options.verbose) { console.log(`${color.cyan('ℹ')} Asserts: ${color.green(String(assertions.passed))} passed, ${color[assertions.failed ? 'red' : 'green'](String(assertions.failed))} failed`); } console.log(`${color.cyan('ℹ')} Tests: ${color.green(String(tests.passed))} passed, ${color[tests.failed ? 'red' : 'green'](String(tests.failed))} failed${tests.skipped ? `, ${color.yellow(String(tests.skipped))} skipped` : ''}`); }; this.root = new Describer(ROOT_DESCRIBER_ID, new Flags().flags); this.current = this.root; this.stack = [this.current]; this.title = title; } } /* EXPORT */ export default Suiter;