import { Failure } from '@ephox/bedrock-common'; import * as AsyncActions from '../pipe/AsyncActions'; import * as GeneralActions from '../pipe/GeneralActions'; import { type DieFn, type NextFn, Pipe, type RunFn } from '../pipe/Pipe'; import { addLogging, type GuardFn } from './Guard'; import { addLogEntry, TestLogs } from './TestLogs'; export interface Step { runStep: (value: T, next: NextFn, die: DieFn, logs: TestLogs) => void; } const raw = (f: RunFn): Step => ({ runStep: Pipe(f) }); const stateful = (f: (v: T, next: (v: U) => void, die: (err) => void) => void): Step => raw((value: T, next: NextFn, die: DieFn, logs: TestLogs) => { f( value, (nextValue: U) => next(nextValue, logs), (err) => die(err, logs) ); }); // Chiefly used for limiting things with timeouts. const control = (step: Step, guard: GuardFn): Step => raw((value: T, next: NextFn, die: DieFn, logs: TestLogs) => { guard(step.runStep, value, next, die, logs); }); const sync = (f: () => void): Step => raw((value: T, next: NextFn, die: DieFn, logs: TestLogs) => { f(); next(value, logs); }); const async = (f: (next: () => void, die: (err) => void) => void): Step => raw((value: T, next: NextFn, die: DieFn, logs: TestLogs) => { f( () => next(value, logs), (err) => die(err, logs) ); }); // Convenience functions const debugging: Step = sync(GeneralActions.debug); const log = (message: string): Step => raw((value: T, next: NextFn, die: DieFn, logs: TestLogs) => { // eslint-disable-next-line no-console console.log(message); next(value, addLogEntry(logs, message)); }); const label = (label: string, chain: Step): Step => control(chain, addLogging(label)); const wait = (amount: number): Step => async(AsyncActions.delay(amount)); const fail = (message: string): Step => async(AsyncActions.fail(message)); const pass: Step = sync(GeneralActions.pass); const predicate = (p: (value: T) => boolean): Step => stateful((value: T, next, die) => { p(value) ? next(value) : die('predicate did not succeed'); }); const toPromise = (step: Step) => (a: A): Promise => { return new Promise(((resolve, reject) => { step.runStep(a, (b, _logs) => { // TODO: What to do with logs? We lose them. resolve(b); }, (err, logs) => { reject(Failure.prepFailure(err, logs)); }, TestLogs.init() ); })); }; const fromPromise = (p: () => Promise): Step => Step.async((next, die) => { p().then(next, die); }); export const Step = { stateful, control, sync, async, debugging, log, label, wait, fail, pass, raw, predicate, toPromise, fromPromise };