import { IRunContext , IRunResult } from "asciiflow/testing" ; export type IHookFunction = ( ) => any ; export interface IHookOptions { name : string ; timeout ? : number ; } export function hook ( name , fn ) ; export function hook ( name , options , fn ) ; export function hook ( nameOrOptions , optionsOrFn , fn ? ) { return Hook . create ( nameOrOptions , optionsOrFn , fn ) ; } export type IHookHandler = typeof Hook . create ; export class Hook { public static readonly DEFAULT_TIMEOUT_MILLIS = 30000 ; public static create ( nameOrOptions , optionsOrFn , fn ? ) { let options = typeof nameOrOptions === "string" ? { name : nameOrOptions } : nameOrOptions ; if ( typeof optionsOrFn === "function" ) { fn = optionsOrFn ; } else { options = { ... options , ... optionsOrFn } ; } return new Hook ( options , fn ) ; } constructor ( public readonly options , private readonly fn ) { } public async run ( ctx ) { let timer ; const timeout = this . options . timeout || Hook . DEFAULT_TIMEOUT_MILLIS ; const result = { path : [ ... ctx . path , `${ this . options . name } (hook)` ] , outcome : "failed" } ; try { await Promise . race ( [ this . fn ( ) , new Promise ( ( _ , reject ) => { timer = setTimeout ( ( ) => { result . outcome = "timeout" ; reject ( new Error ( `Timed out (${ timeout }ms).` ) ) ; } , timeout ) ; } ) ] ) ; result . outcome = "passed" ; } catch ( err ) { ctx . results . push ( { ... result , err } ) ; throw err ; } finally { clearTimeout ( timer ) ; } } }