import * as ErrorTypes from '../alien/ErrorTypes'; import type { DieFn, NextFn, RunFn } from '../pipe/Pipe'; import * as Logger from './Logger'; import { addLogEntry, type TestLogs } from './TestLogs'; export type GuardFn = (run: RunFn, value: T, next: NextFn, die: DieFn, logs: TestLogs) => void; const defaultInterval = 10; const defaultAmount = 3000; const tryUntilNot = (label: string, interval: number = defaultInterval, amount: number = defaultAmount): GuardFn => (f: RunFn, value: T, next: NextFn, die: DieFn, logs: TestLogs) => { const repeat = (startTime: number) => { f(value, (v, newLogs) => { const elapsed = Date.now() - startTime; if (elapsed >= amount) { die( new Error('Waited for ' + amount + 'ms for something to be unsuccessful. ' + label), addLogEntry(newLogs, 'WaitErr: ' + label + ' = Failed (after ' + amount + 'ms)') ); } else { setTimeout(() => { repeat(startTime); }, interval); } }, (err, newLogs) => { // Note, this one is fairly experimental. // Because errors cause die as well, this is not always the best option. // What we do is check to see if it is an error prototype. if (Error.prototype.isPrototypeOf(err)) { die(err, newLogs); } else { next(value, addLogEntry(newLogs, 'WaitErr: ' + label + ' = SUCCESS!')); } }, logs); }; repeat(Date.now()); }; const tryUntil = (label: string, interval: number = defaultInterval, amount: number = defaultAmount): GuardFn => (f: RunFn, value: T, next: NextFn, die: DieFn, logs: TestLogs) => { const repeat = (startTime: number) => { f(value, (v, newLogs) => { next(v, addLogEntry(newLogs, 'Wait: ' + label + ' = SUCCESS!')); }, (err, newLogs) => { const elapsed = Date.now() - startTime; if (elapsed >= amount) { die( ErrorTypes.enrichWith('Waited for ' + amount + 'ms for something to be successful. ' + label, err), addLogEntry(newLogs, 'Wait: ' + label + ' = FAILED (after ' + amount + 'ms)') ); } else { setTimeout(() => { repeat(startTime); }, interval); } }, logs); }; repeat(Date.now()); }; const timeout = (label: string, limit: number): GuardFn => (f: RunFn, value: T, next: NextFn, die: DieFn, logs: TestLogs) => { let passed = false; let failed = false; const hasNotExited = () => passed === false && failed === false; const timer = setTimeout(() => { if (hasNotExited()) { failed = true; die('Hit the limit (' + limit + ') for: ' + label, logs); } }, limit); f(value, (v: U, newLogs: TestLogs) => { clearTimeout(timer); if (hasNotExited()) { passed = true; next(v, newLogs); } }, (err, newLogs) => { if (hasNotExited()) { failed = true; die(err, newLogs); } }, logs); }; const addLogging = (label: string): GuardFn => (f: RunFn, value: T, next: NextFn, die: DieFn, logs: TestLogs) => Logger.t(label, { runStep: f }).runStep(value, next, die, logs); export { timeout, tryUntil, tryUntilNot, addLogging };