(t, idx, total)
| 1045 | |
| 1046 | // ---------- Runner ---------- |
| 1047 | async function runOne(t, idx, total) { |
| 1048 | setStatus(`running (${idx + 1}/${total}): ${t.name}`); |
| 1049 | if (!t.manual) showProgress(t.name); |
| 1050 | const title = `• ${t.name}`; |
| 1051 | const t0 = performance.now(); |
| 1052 | try { |
| 1053 | logLine(`▶️ <b>${escapeHtml(t.name)}</b>`); |
| 1054 | await t.run(); |
| 1055 | pass(`${title} (${fmtMs(performance.now() - t0)})`); |
| 1056 | } catch (e) { |
| 1057 | const extra = e && e.stack ? e.stack : String(e); |
| 1058 | const msg = String(e && e.message || e); |
| 1059 | if (msg.startsWith("SKIP:")) { |
| 1060 | // Soft outcome — count as skip, not as fail. |
| 1061 | skip(`${title} (${fmtMs(performance.now() - t0)}) — ${msg.slice(5).trim()}`); |
| 1062 | } else { |
| 1063 | fail(`${title} (${fmtMs(performance.now() - t0)})`, extra); |
| 1064 | } |
| 1065 | } finally { |
| 1066 | hideProgress(); |
| 1067 | // Any leftover awaiting bar from a thrown-mid-flight test gets cleaned up. |
| 1068 | hideAwaiting(); |
| 1069 | } |
| 1070 | } |
| 1071 | |
| 1072 | let running = false; |
| 1073 | function setAllButtonsDisabled(disabled) { |
no test coverage detected