UNPKG

functionalscript

Version:

FunctionalScript is a purely functional subset of JavaScript

96 lines (95 loc) 4.29 kB
import { entries, fold } from "../../types/list/module.f.js"; import { reset, fgGreen, fgRed, bold } from "../../text/sgr/module.f.js"; import { env, loadModuleMap } from "../module.f.js"; const isTest = (s) => s.endsWith('test.f.js') || s.endsWith('test.f.ts'); const addPass = (delta) => (ts) => ({ ...ts, time: ts.time + delta, pass: ts.pass + 1 }); const addFail = (delta) => (ts) => ({ ...ts, time: ts.time + delta, fail: ts.fail + 1 }); const timeFormat = (a) => { const y = Math.round(a * 10_000).toString(); const yl = 5 - y.length; const x = '0'.repeat(yl > 0 ? yl : 0) + y; const s = x.length - 4; const b = x.substring(0, s); const e = x.substring(s); return `${b}.${e} ms`; }; export const test = (input) => { let { moduleMap, log, error, measure, tryCatch, env, state } = input; const isGitHub = env('GITHUB_ACTION') !== undefined; const f = ([k, v]) => { const test = i => v => ([ts, state]) => { const next = test(`${i}| `); switch (typeof v) { case 'function': { if (v.length === 0) { const [[s, r], delta, state0] = measure(() => tryCatch(v))(state); state = state0; // Usual tests throw on error, but if the function name is 'throw', then the test passes if it throws. if ((s === 'error') === (v.name !== 'throw')) { ts = addFail(delta)(ts); if (isGitHub) { // https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions // https://github.com/OndraM/ci-detector/blob/main/src/Ci/GitHubActions.php state = error(`::error file=${k},line=1,title=[3]['a']()::${r}`)(state); } else { state = error(`${i}() ${fgRed}error${reset}, ${timeFormat(delta)}`)(state); state = error(`${fgRed}${r}${reset}`)(state); } } else { ts = addPass(delta)(ts); state = log(`${i}() ${fgGreen}ok${reset}, ${timeFormat(delta)}`)(state); } [ts, state] = next(r)([ts, state]); } break; } case 'object': { if (v !== null) { const f = ([k, v]) => ([time, state]) => { state = log(`${i}${k}:`)(state); [time, state] = next(v)([time, state]); return [time, state]; }; [ts, state] = fold(f)([ts, state])(v instanceof Array ? entries(v) : Object.entries(v)); } break; } } return [ts, state]; }; return ([ts, state]) => { if (isTest(k)) { state = log(`testing ${k}`)(state); [ts, state] = test('| ')(v.default)([ts, state]); } return [ts, state]; }; }; let ts = { time: 0, pass: 0, fail: 0 }; [ts, state] = fold(f)([ts, state])(Object.entries(moduleMap)); const fgFail = ts.fail === 0 ? fgGreen : fgRed; state = log(`${bold}Number of tests: pass: ${fgGreen}${ts.pass}${reset}${bold}, fail: ${fgFail}${ts.fail}${reset}${bold}, total: ${ts.pass + ts.fail}${reset}`)(state); state = log(`${bold}Time: ${timeFormat(ts.time)}${reset}`)(state); return [ts.fail !== 0 ? 1 : 0, state]; }; export const anyLog = (f) => (s) => (state) => { f(s); return state; }; export const measure = (p) => (f) => (state) => { const b = p.now(); const r = f(); const e = p.now(); return [r, e - b, state]; }; export const main = async (io) => test({ moduleMap: await loadModuleMap(io), log: anyLog(io.console.log), error: anyLog(io.console.error), measure: measure(io.performance), tryCatch: io.tryCatch, env: env(io), state: undefined, })[0];