import { invoke } from "@tauri-apps/api/core"; import type { SelfTestReport } from "./lib/selfTest"; import type { StepResult } from "./lib/selfTestSteps"; /** * Unattended self-test mode, enabled by the VOICED_SELFTEST_OUT environment variable (the backend * returns the report path, or null in normal use). It runs the diagnostics and an end-to-end invoice * flow in this very webview, writes a JSON report and makes the process exit with the verdict. */ const WATCHDOG_MS = 120_000; export async function maybeRunSelfTest(): Promise { let reportPath: string | null; try { reportPath = await invoke("selftest_config"); } catch { return; // Not running inside Tauri (plain browser dev server). } if (!reportPath) return; const path = reportPath; const start = performance.now(); const startedAt = new Date().toISOString(); let diagnostics: SelfTestReport | null = null; let steps: StepResult[] = []; let renderMode: () => string = () => "unknown"; let finished = false; const finish = async (ok: boolean, error?: string) => { if (finished) return; finished = true; const body = { ok, ...(error ? { error } : {}), startedAt, totalMs: Math.round(performance.now() - start), userAgent: navigator.userAgent, renderMode: renderMode(), steps, diagnostics, }; try { await invoke("selftest_report", { json: JSON.stringify(body, null, 2), ok }); } catch (e) { console.error("self-test report could not be written", e); } }; const failWith = (what: string) => void finish(false, what); const timer = window.setTimeout(() => { if (finished) return; void finish(false, "timeout"); }, WATCHDOG_MS); window.addEventListener("error", (e) => failWith(`uncaught error: ${e.message}`)); window.addEventListener("unhandledrejection", (e) => failWith(`unhandled rejection: ${String(e.reason)}`)); try { const [{ createStepLog }, { runSelfTest }, { browserSelfTestDeps }, { runE2eSteps }, { getRenderClient }] = await Promise.all([ import("./lib/selfTestSteps"), import("./lib/selfTest"), import("./lib/selfTestBrowser"), import("./lib/selfTestE2e"), import("./lib/renderClient"), ]); renderMode = () => getRenderClient().mode; const log = createStepLog(); steps = log.steps; await log.run("diagnostics: render, rasterise, fingerprint, flatten", async () => { const report = await runSelfTest(browserSelfTestDeps(), (r) => { diagnostics = r; }); diagnostics = report; const failed = report.sections.flatMap((s) => s.checks.filter((c) => c.status === "fail").map((c) => `${s.title} / ${c.label}: ${c.detail ?? ""}`)); return { ok: report.passed, detail: failed.length ? failed.join("\n") : "all checks passed" }; }); await runE2eSteps(log, path); window.clearTimeout(timer); await finish(log.steps.every((s) => s.ok)); } catch (e) { window.clearTimeout(timer); await finish(false, e instanceof Error ? `${e.name}: ${e.message}\n${e.stack ?? ""}` : String(e)); } }