fix(install-e2e): verify input readiness and tolerate transient windows

This commit is contained in:
ethernet
2026-09-06 12:55:05 -04:00
parent 7cfe82a2a7
commit cf9d341e4b
5 changed files with 42 additions and 7 deletions

View File

@@ -6,6 +6,28 @@ const { prepareWindowForInput } = createRequire(import.meta.url)(
'../../../tests/install/e2e-assets/window-input.cjs',
)
test('does not finish when IPC reports 100% before the window factor settles', async () => {
let observations = 0
const previous = (globalThis as any).hermesDesktop
;(globalThis as any).hermesDesktop = { zoom: {
setPercent: () => undefined,
get: async () => ({ percent: 100 }),
} }
const appWindow = { evaluate: async (fn: any) => fn({ webContents: {
getZoomFactor: () => ++observations === 1 ? 0.9 : 1,
} }) }
const page = {
evaluate: async (fn: any) => fn(),
waitForTimeout: async () => undefined,
}
try {
await prepareWindowForInput({ browserWindow: async () => appWindow }, page)
expect(observations).toBeGreaterThan(1)
} finally {
;(globalThis as any).hermesDesktop = previous
}
})
test('reapplies zoom when startup overwrites the first request', async () => {
let requests = 0
let factor = 0.9

View File

@@ -161,6 +161,8 @@ async function main() {
let iter = 0
while (!openedSettings) {
// A boot-time restore or focus event can move the scale after preparation.
await prepareWindowForInput(app, page)
iter++
for (const make of laterLocators) {
try {

View File

@@ -199,14 +199,16 @@ if launchFound {
}
Log("Launch clicked; waiting for the Hermes desktop app window")
; The installer spawns Hermes.exe detached and exits itself.
; WinWait returns 0 on timeout; it does not throw. The old unchecked return
; led to WinGetPos throwing "Target window not found." Reuse the bounded
; real-window poll so transient handles are ignored and failures name the wait.
; CI's installer remained on LAUNCHING past 120s after a successful bootstrap.
try {
WinWait(appWin, , 120)
appRect := WaitForRealWindow(appWin, 300000)
} catch {
throw Error("Hermes.exe window did not appear within 120s of clicking Launch")
throw Error("Hermes.exe real-sized window did not appear within 300s of clicking Launch")
}
WinGetPos(&ax, &ay, &aw, &ah, appWin)
Log(Format("App window appeared at x={1} y={2} w={3} h={4}", ax, ay, aw, ah))
Log(Format("App window appeared at x={1} y={2} w={3} h={4}", appRect.x, appRect.y, appRect.w, appRect.h))
Sleep(8000) ; let the renderer paint (recorded as proof)
Log("done")

View File

@@ -216,6 +216,7 @@ async function main() {
}
}).then((d) => JSON.stringify(d)).catch((e) => `hit-dump failed: ${e.message}`)
for (let iter = 1; ; iter++) {
await prepareWindowForInput(app, window);
await later
.click({ timeout: 2_000 })
.then(async () => {
@@ -225,6 +226,9 @@ async function main() {
.catch((e) => log(`[overlay] iter ${iter} chooseLater click failed: ${brief(e)}`))
try {
await settingsButton.click({ timeout: 4_000 })
// A landed click during shell hydration can be lost on a remount.
// Confirm the destination before looking for its About control.
await window.waitForURL(/[#/]settings(?:[/?]|$)/, { timeout: 4_000 })
settingsOpened = true
break
} catch (e) {

View File

@@ -21,8 +21,13 @@ async function prepareWindowForInput(app, page) {
globalThis.hermesDesktop.zoom.setPercent(100)
return globalThis.hermesDesktop.zoom.get()
})
if (state.percent === 100) break
if (Date.now() >= deadline) throw new Error('timed out waiting for 100% app window zoom')
// The renderer IPC and BrowserWindow can observe different moments of
// startup restoration. Both must agree before the driver sends input.
const factor = await window.evaluate(win => win.webContents.getZoomFactor())
if (state.percent === 100 && Math.abs(factor - 1) < 0.001) return
if (Date.now() >= deadline) {
throw new Error(`timed out waiting for 100% app window zoom (IPC ${state.percent}%, factor ${factor})`)
}
await page.waitForTimeout(100)
}
} else {