test(install-e2e): navigate historical updates and permit unattended quit
This commit is contained in:
@@ -289,7 +289,8 @@ test('driver strips caller secrets and records missing executables as failure wi
|
||||
try {
|
||||
const env = smokeEnvironment({ PATH: '/usr/bin', DISPLAY: ':1', OPENAI_API_KEY: 'secret', HERMES_DESKTOP_BOOT_FAKE: '1',
|
||||
HERMES_DESKTOP_HERMES_ROOT: '/wrong', PYTHONPATH: '/wrong', NODE_OPTIONS: '--inspect', HERMES_HOME: '/wrong' }, home, path.join(home, 'user-data'))
|
||||
expect(env).toMatchObject({ PATH: '/usr/bin', DISPLAY: ':1', HERMES_HOME: home })
|
||||
expect(env).toMatchObject({ PATH: '/usr/bin', DISPLAY: ':1', HERMES_HOME: home,
|
||||
HERMES_DESKTOP_SKIP_QUIT_CONFIRM: '1' })
|
||||
for (const key of ['OPENAI_API_KEY', 'HERMES_DESKTOP_BOOT_FAKE', 'HERMES_DESKTOP_HERMES_ROOT', 'PYTHONPATH', 'NODE_OPTIONS']) {
|
||||
expect(env[key]).toBeUndefined()
|
||||
}
|
||||
|
||||
72
tests-js/update-ui.test.mjs
Normal file
72
tests-js/update-ui.test.mjs
Normal file
@@ -0,0 +1,72 @@
|
||||
import { JSDOM } from 'jsdom'
|
||||
import { afterEach, expect, test, vi } from 'vitest'
|
||||
import updateUi from '../tests/install/e2e-assets/update-ui.cjs'
|
||||
|
||||
const windows = []
|
||||
afterEach(() => {
|
||||
windows.splice(0).forEach(window => window.close())
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
function fixture({ details = true, available = true } = {}) {
|
||||
vi.useFakeTimers({ toFake: ['Date'] })
|
||||
vi.setSystemTime(0)
|
||||
const { window } = new JSDOM('<body></body>', { runScripts: 'outside-only' })
|
||||
windows.push(window)
|
||||
const { document } = window
|
||||
const clicks = []
|
||||
const addButton = (text, onClick) => {
|
||||
const button = document.createElement('button')
|
||||
button.textContent = text
|
||||
button.onclick = () => { clicks.push(text); onClick?.() }
|
||||
document.body.append(button)
|
||||
return button
|
||||
}
|
||||
const revealUpdate = () => addButton('Update now')
|
||||
const check = addButton('Check now', () => {
|
||||
check.disabled = true
|
||||
if (!available) return
|
||||
if (details) {
|
||||
const more = addButton("See what's new", () => { more.remove(); revealUpdate() })
|
||||
} else {
|
||||
revealUpdate()
|
||||
}
|
||||
})
|
||||
const status = { supported: true, behind: available ? 1 : 0 }
|
||||
window.hermesDesktop = { updates: { check: async () => status } }
|
||||
const page = {
|
||||
getByRole(role, { name }) {
|
||||
expect(role).toBe('button')
|
||||
const selected = () => [...document.querySelectorAll('button')].find(button => name.test(button.textContent))
|
||||
const locator = {
|
||||
first: () => locator,
|
||||
isVisible: async () => Boolean(selected()),
|
||||
click: async () => {
|
||||
const button = selected()
|
||||
if (!button || button.disabled) throw new Error('button not actionable')
|
||||
button.click()
|
||||
},
|
||||
}
|
||||
return locator
|
||||
},
|
||||
waitForTimeout: async ms => vi.setSystemTime(Date.now() + ms),
|
||||
evaluate: async fn => window.eval(`(${fn.toString()})()`),
|
||||
}
|
||||
return { page, clicks, log: vi.fn(), shot: vi.fn() }
|
||||
}
|
||||
|
||||
test.each([true, false])('reveals the actual update button without applying (details=%s)', async details => {
|
||||
const f = fixture({ details })
|
||||
const update = await updateUi.waitForUpdate(f.page, f)
|
||||
expect(await update.isVisible()).toBe(true)
|
||||
expect(f.clicks).toEqual(details ? ['Check now', "See what's new"] : ['Check now'])
|
||||
expect(f.shot).toHaveBeenCalledWith(f.page, '04-update-available')
|
||||
})
|
||||
|
||||
test('does not turn a completed check without an update into success', async () => {
|
||||
const f = fixture({ available: false })
|
||||
await expect(updateUi.waitForUpdate(f.page, f)).rejects.toThrow(/"Update now" never appeared/)
|
||||
expect(f.clicks).toEqual(['Check now'])
|
||||
expect(f.log).toHaveBeenCalledWith('[update-status] {"supported":true,"behind":0}')
|
||||
expect(f.shot).toHaveBeenCalledWith(f.page, 'ERROR-no-update-now')
|
||||
})
|
||||
@@ -45,6 +45,9 @@ export function smokeEnvironment(inherited, home, userData) {
|
||||
XDG_DATA_HOME: path.join(home, '.desktop-smoke-home', '.local', 'share'),
|
||||
XDG_CACHE_HOME: path.join(home, '.desktop-smoke-home', '.cache'),
|
||||
HERMES_HOME: home, HERMES_DESKTOP_USER_DATA_DIR: userData,
|
||||
// app.close() cannot answer a native modal. This bypasses confirmation only;
|
||||
// normal backend teardown and the driver's process-exit checks still run.
|
||||
HERMES_DESKTOP_SKIP_QUIT_CONFIRM: '1',
|
||||
APPDATA: path.join(home, '.desktop-smoke-home', 'AppData', 'Roaming'),
|
||||
LOCALAPPDATA: path.join(home, '.desktop-smoke-home', 'AppData', 'Local'),
|
||||
};
|
||||
|
||||
@@ -80,8 +80,14 @@ async function openAbout(page, { prepare, log, shot, confirmSettings = false, hi
|
||||
|
||||
async function waitForUpdate(page, { log, shot }) {
|
||||
const update = page.getByRole('button', { name: /update now/i }).first()
|
||||
const details = page.getByRole('button', { name: /^see what['’]s new$/i }).first()
|
||||
const deadline = Date.now() + 180_000
|
||||
while (!await update.isVisible().catch(() => false) && Date.now() < deadline) {
|
||||
if (await details.isVisible().catch(() => false)) {
|
||||
await details.click({ timeout: 5_000 })
|
||||
log("opened See what's new")
|
||||
continue
|
||||
}
|
||||
await page.getByRole('button', { name: /check now/i }).first().click({ timeout: 5_000 })
|
||||
.then(() => log('nudged Check now')).catch(() => {})
|
||||
await page.waitForTimeout(15_000)
|
||||
@@ -92,7 +98,7 @@ async function waitForUpdate(page, { log, shot }) {
|
||||
).catch(error => `updates.check failed: ${error.message}`)
|
||||
log(`[update-status] ${JSON.stringify(status)}`)
|
||||
await shot(page, 'ERROR-no-update-now')
|
||||
throw new Error('"Update now" never appeared — update check did not report an update')
|
||||
throw new Error('"Update now" never appeared after checking for updates and opening available update details')
|
||||
}
|
||||
await shot(page, '04-update-available')
|
||||
return update
|
||||
|
||||
Reference in New Issue
Block a user