fix(install-e2e): await zoom IPC outside Playwright's predicate

Playwright 1.58 accepts a Promise-valued waitForFunction predicate before its false result. Await each zoom read explicitly; cover delayed responses and fresh-install startup.
This commit is contained in:
ethernet
2026-09-06 00:00:56 -04:00
parent aef539b47a
commit 184b2aebab
3 changed files with 80 additions and 6 deletions

View File

@@ -1,14 +1,39 @@
import { readFileSync } from 'node:fs'
import { readFileSync, unlinkSync, writeFileSync } from 'node:fs'
import { createRequire } from 'node:module'
import path from 'node:path'
import { setupNoProvider } from './fixtures'
import { buildAppEnv, createSandbox, launchDesktop, setupNoProvider } from './fixtures'
import { type ElectronApplication, expect, type Page, test } from './test'
const { prepareWindowForInput } = createRequire(import.meta.url)(
'../../../tests/install/e2e-assets/window-input.cjs',
) as { prepareWindowForInput: (app: ElectronApplication, page: Page) => Promise<void> }
test('input setup survives a fresh-install zoom restore before onboarding', async () => {
const sandbox = createSandbox('cold-input')
unlinkSync(path.join(sandbox.userDataDir, 'zoom-state.json'))
writeFileSync(path.join(sandbox.hermesHome, 'config.yaml'), '# no provider\n', 'utf8')
let app: ElectronApplication | undefined
try {
const launched = await launchDesktop(buildAppEnv(sandbox))
app = launched.app
const page = launched.page
await page.waitForSelector('button', { state: 'attached' })
await prepareWindowForInput(app, page)
const later = page.getByRole('button', { name: /choose a provider later/i })
await expect(later).toBeVisible({ timeout: 60_000 })
const appWindow = await app.browserWindow(page)
await appWindow.evaluate(win => win.emit('focus'))
await expect.poll(() => appWindow.evaluate(win => win.webContents.getZoomFactor())).toBeCloseTo(1)
await later.click({ timeout: 5_000 })
await expect(later).toBeHidden()
} finally {
await app?.close().catch(() => undefined)
sandbox.cleanup()
}
})
// Exercise the install driver's input setup against the real renderer/backend,
// with no installer, update, credentials, or live user data.
for (const lifecycleEvent of ['focus', 'navigation'] as const) {
@@ -17,6 +42,7 @@ for (const lifecycleEvent of ['focus', 'navigation'] as const) {
const { app, page, sandbox } = fixture
try {
await prepareWindowForInput(app, page)
const later = page.getByRole('button', { name: /choose a provider later/i })
await expect(later).toBeVisible({ timeout: 60_000 })
const zoomFile = path.join(sandbox.userDataDir, 'zoom-state.json')

View File

@@ -0,0 +1,43 @@
import { createRequire } from 'node:module'
import { expect, test } from 'vitest'
const { prepareWindowForInput } = createRequire(import.meta.url)(
'../../../tests/install/e2e-assets/window-input.cjs',
)
test('awaits the zoom response instead of accepting a truthy Promise', async () => {
let reads = 0
let factor = 0.9
const zoom = {
setPercent: () => undefined,
get: async () => {
reads++
if (reads > 1) {factor = 1}
return { percent: factor * 100 }
},
}
const previous = (globalThis as any).hermesDesktop
;(globalThis as any).hermesDesktop = { zoom }
const window = { evaluate: async (fn: any) => fn({ webContents: { getZoomFactor: () => factor } }) }
const page = {
evaluate: async (fn: any) => fn(),
// Playwright 1.58 accepts the predicate's Promise before it resolves.
waitForFunction: async (fn: any) => { await fn() },
waitForTimeout: async () => undefined,
}
try {
await prepareWindowForInput({ browserWindow: async () => window }, page)
expect(reads).toBeGreaterThan(1)
expect(factor).toBe(1)
} finally {
;(globalThis as any).hermesDesktop = previous
}
})

View File

@@ -11,10 +11,15 @@ async function prepareWindowForInput(app, page) {
return true
})
if (persistent) {
await page.waitForFunction(async () => {
const state = await globalThis.hermesDesktop.zoom.get()
return state.percent === 100
}, undefined, { timeout: 15_000 })
// Playwright 1.58 treats an async waitForFunction predicate's Promise as
// truthy even when it resolves false. Await each IPC read on the driver.
const deadline = Date.now() + 15_000
for (;;) {
const state = await page.evaluate(() => globalThis.hermesDesktop.zoom.get())
if (state.percent === 100) break
if (Date.now() >= deadline) throw new Error('timed out waiting for 100% app window zoom')
await page.waitForTimeout(100)
}
} else {
// Older sampled releases have no zoom preference bridge.
await window.evaluate(win => win.webContents.setZoomLevel(0))