From 7ffc26ec563de453f7e827b873a4d959e3327d82 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Tue, 11 Aug 2026 03:01:52 -0700 Subject: [PATCH] fix(e2e): detect update hand-off via marker file, not Playwright close event Attempt 8 drove the ENTIRE GUI update click-path successfully: onboarding dismissed, Settings opened, About opened, Update now clicked, updating overlay shown. The hand-off log proves the real update then ran: desktop (pid 8880) exited, venv unlocked, 'hermes update --yes --gateway --force --branch main' fetched from serve.git, found 1 new commit, pulled, and restored. Everything worked. The only failure was the driver waiting on Playwright's app 'close' event, which doesn't fire reliably when the Electron app self-quits for the hand-off. Switch to the authoritative signal: poll for the HERMES_HOME/.hermes-update-in-progress marker (or the result JSON, or a genuine window-gone), which the hand-off writes ~4s after the click. The PowerShell driver still owns asserting the OUTCOME (target sha, marker cleanup, working hermes, relaunched app) after the driver returns. --- tests/install/e2e-assets/drive-update.cjs | 66 +++++++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/tests/install/e2e-assets/drive-update.cjs b/tests/install/e2e-assets/drive-update.cjs index 5eecdb6a59..25d436072d 100644 --- a/tests/install/e2e-assets/drive-update.cjs +++ b/tests/install/e2e-assets/drive-update.cjs @@ -201,20 +201,64 @@ async function main() { await page.waitForTimeout(1200) await shot(page, '05-updating-overlay') - // ── Wait for the app to quit for the hand-off ───────────────────────── - await new Promise((resolve, reject) => { - const t = setTimeout( - () => reject(new Error('app did not quit within 120s of Update now — hand-off did not start')), - 120_000 - ) + // ── Wait for the hand-off to take over ──────────────────────────────── + // Clicking Update now spawns the detached updater (desktop-update.ps1 or + // the staged binary), which claims HERMES_HOME/.hermes-update-in-progress + // and then the desktop quits. We do NOT rely on Playwright's app 'close' + // event: when the app self-quits for the hand-off that event is + // unreliable (attempt 8 timed out on it even though the hand-off log + // proved the desktop had exited and `hermes update` was already running). + // + // The authoritative "hand-off started" signal is the marker file (or the + // result JSON, if the whole update finished fast). Poll for either, and + // also accept a genuine app close. Any one is success — the PowerShell + // driver owns asserting the update's OUTCOME (sha, marker cleanup, + // relaunch) after we return. + const hermesHome = process.env.HERMES_HOME + const markerPath = hermesHome ? path.join(hermesHome, '.hermes-update-in-progress') : null + const resultPath = hermesHome ? path.join(hermesHome, '.hermes-update-result.json') : null - app.on('close', () => { - clearTimeout(t) - resolve() - }) + let appClosed = false + app.on('close', () => { + appClosed = true }) - log('app quit for updater hand-off — success, the detached updater owns the rest') + const handoffDeadline = Date.now() + 150_000 + let handoffStarted = false + + while (Date.now() < handoffDeadline) { + if (markerPath && fs.existsSync(markerPath)) { + log('hand-off marker present — updater has taken over') + handoffStarted = true + break + } + if (resultPath && fs.existsSync(resultPath)) { + log('update result JSON already present — updater finished fast') + handoffStarted = true + break + } + if (appClosed) { + log('app closed — hand-off in progress') + handoffStarted = true + break + } + // Secondary: if the renderer window is gone, evaluate throws. + try { + await page.evaluate(() => true) + } catch { + log('renderer window gone — app quit for hand-off') + handoffStarted = true + break + } + await new Promise(r => setTimeout(r, 2000)) + } + + if (!handoffStarted) { + await shot(page, 'ERROR-no-handoff') + throw new Error('no hand-off within 150s of Update now (no marker, no result, app still alive)') + } + + log('hand-off confirmed — detached updater owns the rest') } main()