orca/src/main/updater.mac-install.test.ts

207 lines
5.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
const {
appMock,
browserWindowMock,
nativeUpdaterMock,
autoUpdaterMock,
shellMock,
isMock,
killAllPtyMock
} = vi.hoisted(() => {
const appEventHandlers = new Map<string, ((...args: unknown[]) => void)[]>()
const eventHandlers = new Map<string, ((...args: unknown[]) => void)[]>()
const appOn = vi.fn((event: string, handler: (...args: unknown[]) => void) => {
const handlers = appEventHandlers.get(event) ?? []
handlers.push(handler)
appEventHandlers.set(event, handlers)
return appMock
})
const appEmit = (event: string, ...args: unknown[]) => {
for (const handler of appEventHandlers.get(event) ?? []) {
handler(...args)
}
}
const on = vi.fn((event: string, handler: (...args: unknown[]) => void) => {
const handlers = eventHandlers.get(event) ?? []
handlers.push(handler)
eventHandlers.set(event, handlers)
return autoUpdaterMock
})
const emit = (event: string, ...args: unknown[]) => {
for (const handler of eventHandlers.get(event) ?? []) {
handler(...args)
}
}
const reset = () => {
appEventHandlers.clear()
appOn.mockClear()
eventHandlers.clear()
on.mockClear()
autoUpdaterMock.checkForUpdates.mockReset()
autoUpdaterMock.downloadUpdate.mockReset()
autoUpdaterMock.quitAndInstall.mockReset()
}
const autoUpdaterMock = {
autoDownload: false,
autoInstallOnAppQuit: false,
on,
checkForUpdates: vi.fn(),
downloadUpdate: vi.fn(),
quitAndInstall: vi.fn(),
setFeedURL: vi.fn(),
emit,
reset
}
return {
appMock: {
isPackaged: true,
getVersion: vi.fn(() => '1.0.51'),
on: appOn,
emit: appEmit,
quit: vi.fn()
},
browserWindowMock: {
getAllWindows: vi.fn(() => [])
},
nativeUpdaterMock: {
on: vi.fn()
},
autoUpdaterMock,
shellMock: {
openExternal: vi.fn()
},
isMock: { dev: false },
killAllPtyMock: vi.fn()
}
})
vi.mock('electron', () => ({
app: appMock,
BrowserWindow: browserWindowMock,
autoUpdater: nativeUpdaterMock,
powerMonitor: { on: vi.fn() },
shell: shellMock,
net: { fetch: vi.fn() }
}))
vi.mock('electron-updater', () => ({
autoUpdater: autoUpdaterMock
}))
vi.mock('@electron-toolkit/utils', () => ({
is: isMock
}))
vi.mock('./ipc/pty', () => ({
killAllPty: killAllPtyMock
}))
vi.mock('./updater-changelog', () => ({
fetchChangelog: vi.fn().mockResolvedValue(null)
}))
vi.mock('./updater-nudge', () => ({
fetchNudge: vi.fn().mockResolvedValue(null),
shouldApplyNudge: vi.fn().mockReturnValue(false)
}))
describe('updater mac install handoff', () => {
beforeEach(() => {
vi.resetModules()
autoUpdaterMock.reset()
nativeUpdaterMock.on.mockReset()
browserWindowMock.getAllWindows.mockReset()
browserWindowMock.getAllWindows.mockReturnValue([])
shellMock.openExternal.mockReset()
appMock.getVersion.mockReset()
appMock.getVersion.mockReturnValue('1.0.51')
appMock.quit.mockReset()
appMock.isPackaged = true
isMock.dev = false
killAllPtyMock.mockReset()
vi.unstubAllGlobals()
vi.useRealTimers()
})
it.runIf(process.platform === 'darwin')(
'waits for Squirrel.Mac before honoring a manual quit that should install the update',
async () => {
const sendMock = vi.fn()
const mainWindow = { webContents: { send: sendMock } }
autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined)
const { setupAutoUpdater } = await import('./updater')
setupAutoUpdater(mainWindow as never)
autoUpdaterMock.emit('update-available', { version: '1.0.61' })
// Why: the update-available handler is now async (it awaits fetchChangelog).
// Flush microtasks so setAvailableVersion runs before update-downloaded fires.
await new Promise((r) => setTimeout(r, 0))
autoUpdaterMock.emit('update-downloaded', { version: '1.0.61' })
const preventDefault = vi.fn()
appMock.emit('before-quit', { preventDefault })
expect(preventDefault).toHaveBeenCalledTimes(1)
expect(autoUpdaterMock.quitAndInstall).not.toHaveBeenCalled()
const nativeDownloadedHandler = nativeUpdaterMock.on.mock.calls.find(
([eventName]) => eventName === 'update-downloaded'
)?.[1] as (() => void) | undefined
expect(nativeDownloadedHandler).toBeTypeOf('function')
nativeDownloadedHandler?.()
expect(autoUpdaterMock.quitAndInstall).toHaveBeenCalledWith(false, true)
expect(sendMock).toHaveBeenCalledWith('updater:status', {
state: 'downloading',
percent: 100,
version: '1.0.61'
})
}
)
it.runIf(process.platform === 'darwin')(
'falls back to a normal quit if Squirrel.Mac never becomes ready',
async () => {
vi.useFakeTimers()
const sendMock = vi.fn()
const mainWindow = { webContents: { send: sendMock } }
autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined)
const { setupAutoUpdater } = await import('./updater')
setupAutoUpdater(mainWindow as never)
autoUpdaterMock.emit('update-available', { version: '1.0.61' })
// Why: the update-available handler is now async (it awaits fetchChangelog).
// Flush microtasks so setAvailableVersion runs before update-downloaded fires.
await vi.advanceTimersByTimeAsync(0)
autoUpdaterMock.emit('update-downloaded', { version: '1.0.61' })
const preventDefault = vi.fn()
appMock.emit('before-quit', { preventDefault })
expect(preventDefault).toHaveBeenCalledTimes(1)
expect(appMock.quit).not.toHaveBeenCalled()
await vi.advanceTimersByTimeAsync(15000)
expect(appMock.quit).toHaveBeenCalledTimes(1)
const secondPreventDefault = vi.fn()
appMock.emit('before-quit', { preventDefault: secondPreventDefault })
expect(secondPreventDefault).not.toHaveBeenCalled()
expect(autoUpdaterMock.quitAndInstall).not.toHaveBeenCalled()
}
)
})