UNPKG

functionalscript

Version:

FunctionalScript is a purely functional subset of JavaScript

118 lines (117 loc) 4.49 kB
import { entries, fold } from "../../types/list/module.f.js"; import { reset, fgGreen, fgRed, bold, stdio, stderr } from "../../text/sgr/module.f.js"; import { env, loadModuleMap } from "../module.f.js"; export 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 parseTestSet = (t) => (x) => { switch (typeof x) { case 'function': { if (x.length === 0) { const xt = x; if (xt.name !== 'throw') { return xt; } // Usual tests throw on error, but if the function name is 'throw', // then the test passes if it throws. return () => { const [tag, value] = t(xt); if (tag === 'ok') { throw value; } return value; }; } break; } case 'object': { if (x !== null) { return Object.entries(x); } break; } } return []; }; export const test = (input) => { let { moduleMap, log, error, measure, tryCatch, env, state } = input; const isGitHub = env('GITHUB_ACTION') !== undefined; const parse = parseTestSet(tryCatch); const f = ([k, v]) => { const test = i => v => ([ts, state]) => { const next = test(`${i}| `); const set = parse(v); if (typeof set === 'function') { const [[s, r], delta, state0] = measure(() => tryCatch(set))(state); state = state0; if (s !== 'ok') { 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 error(`::error file=${k},line=1,title=[3]['a']()::${r}`); } else { error(`${i}() ${fgRed}error${reset}, ${timeFormat(delta)}`); error(`${fgRed}${r}${reset}`); } } else { ts = addPass(delta)(ts); log(`${i}() ${fgGreen}ok${reset}, ${timeFormat(delta)}`); [ts, state] = next(r)([ts, state]); } } else { const f = ([k, v]) => ([time, state]) => { log(`${i}${k}:`); [time, state] = next(v)([time, state]); return [time, state]; }; [ts, state] = fold(f)([ts, state])(set); } return [ts, state]; }; return ([ts, state]) => { if (isTest(k)) { log(`testing ${k}`); [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; log(`${bold}Number of tests: pass: ${fgGreen}${ts.pass}${reset}${bold}, fail: ${fgFail}${ts.fail}${reset}${bold}, total: ${ts.pass + ts.fail}${reset}`); log(`${bold}Time: ${timeFormat(ts.time)}${reset}`); 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: stdio(io), // anyLog(io.console.log), error: stderr(io), // anyLog(io.console.error), measure: measure(io.performance), tryCatch: io.tryCatch, env: env(io), state: undefined, })[0];