| 40 | constructor(public readonly options: IHookOptions, private readonly fn: IHookFunction) {} |
| 41 | |
| 42 | public async run(ctx: IRunContext) { |
| 43 | let timer: NodeJS.Timer; |
| 44 | const timeout = this.options.timeout || Hook.DEFAULT_TIMEOUT_MILLIS; |
| 45 | const result: IRunResult = { |
| 46 | path: [...ctx.path, `${this.options.name} (hook)`], |
| 47 | outcome: "failed" |
| 48 | }; |
| 49 | try { |
| 50 | await Promise.race([ |
| 51 | this.fn(), |
| 52 | new Promise((_, reject) => { |
| 53 | timer = setTimeout(() => { |
| 54 | result.outcome = "timeout"; |
| 55 | reject(new Error(`Timed out (${timeout}ms).`)); |
| 56 | }, timeout); |
| 57 | }) |
| 58 | ]); |
| 59 | result.outcome = "passed"; |
| 60 | } catch (err) { |
| 61 | ctx.results.push({ ...result, err }); |
| 62 | // If hooks fail, we throw anyway. |
| 63 | throw err; |
| 64 | } finally { |
| 65 | clearTimeout(timer); |
| 66 | } |
| 67 | } |
| 68 | } |