functionalscript
Version:
FunctionalScript is a purely functional subset of JavaScript
96 lines (95 loc) • 4.29 kB
JavaScript
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];