( predicate: () => T | undefined | null | false, timeoutMs: number, pollMs = 25, label = '', )
| 117 | } |
| 118 | |
| 119 | function waitFor<T>( |
| 120 | predicate: () => T | undefined | null | false, |
| 121 | timeoutMs: number, |
| 122 | pollMs = 25, |
| 123 | label = '', |
| 124 | ): Promise<T> { |
| 125 | return new Promise((resolve, reject) => { |
| 126 | const started = Date.now(); |
| 127 | const tick = () => { |
| 128 | let v: T | undefined | null | false; |
| 129 | try { v = predicate(); } catch (e) { return reject(e); } |
| 130 | if (v) return resolve(v as T); |
| 131 | if (Date.now() - started > timeoutMs) { |
| 132 | // Name the wait: an async stack loses the await site, so an unlabeled |
| 133 | // timeout can't tell WHICH step flaked (the #662 test's recurring |
| 134 | // timeout was undiagnosable for exactly this reason). |
| 135 | return reject(new Error(`Timed out after ${timeoutMs}ms${label ? ` waiting for: ${label}` : ''}`)); |
| 136 | } |
| 137 | setTimeout(tick, pollMs); |
| 138 | }; |
| 139 | tick(); |
| 140 | }); |
| 141 | } |
| 142 | |
| 143 | function isAlive(pid: number): boolean { |
| 144 | try { process.kill(pid, 0); return true; } catch { return false; } |
no test coverage detected