import { NodeHttpExecutor, serializeToYaml, TmpDir } from "builder-util"
import { AllPublishOptions, DownloadOptions } from "builder-util-runtime"
import { AppUpdater, MacUpdater, NoOpLogger, NsisUpdater } from "electron-updater"
import { TestOnlyUpdaterOptions } from "electron-updater/out/AppUpdater"
import { outputFile, writeFile } from "fs-extra"
import * as path from "path"
import { assertThat } from "./fileAssert"
import { TestAppAdapter } from "./TestAppAdapter"
import { ExpectStatic } from "vitest"
const tmpDir = new TmpDir("updater-test-util")
export async function createTestAppAdapter(version = "0.0.1") {
return new TestAppAdapter(version, await tmpDir.getTempDir())
}
export async function createNsisUpdater(version = "0.0.1") {
const testAppAdapter = await createTestAppAdapter(version)
const result = new NsisUpdater(null, testAppAdapter)
tuneTestUpdater(result)
return result
}
export async function writeUpdateConfig<T extends AllPublishOptions>(data: T): Promise<string> {
const updateConfigPath = path.join(await tmpDir.getTempDir({ prefix: "test-update-config" }), "app-update.yml")
await outputFile(updateConfigPath, serializeToYaml(data))
return updateConfigPath
}
export async function validateDownload(expect: ExpectStatic, updater: AppUpdater, expectDownloadPromise = true) {
const actualEvents = trackEvents(updater)
const updateCheckResult = await updater.checkForUpdates()
const assets = (updateCheckResult?.updateInfo as any).assets
if (assets != null) {
for (const asset of assets) {
delete asset.download_count
}
}
expect(updateCheckResult?.updateInfo).toMatchSnapshot()
if (expectDownloadPromise) {
expect(updateCheckResult?.downloadPromise).toBeDefined()
const downloadResult = await updateCheckResult?.downloadPromise
if (updater instanceof MacUpdater) {
expect(downloadResult).toEqual([])
} else {
await assertThat(expect, path.join(downloadResult![0])).isFile()
}
} else {
expect(updateCheckResult?.downloadPromise).toBeUndefined()
}
expect(actualEvents).toMatchSnapshot()
return updateCheckResult
}
export class TestNodeHttpExecutor extends NodeHttpExecutor {
async download(url: string, destination: string, options: DownloadOptions): Promise<string> {
const obj = new URL(url)
const buffer = await this.downloadToBuffer(obj, options)
await writeFile(destination, buffer)
return buffer.toString()
}
}
export const httpExecutor: TestNodeHttpExecutor = new TestNodeHttpExecutor()
export function tuneTestUpdater(updater: AppUpdater, options?: TestOnlyUpdaterOptions) {
;(updater as any).httpExecutor = httpExecutor
;(updater as any)._testOnlyOptions = {
platform: "win32",
...options,
}
updater.logger = new NoOpLogger()
}
export function trackEvents(updater: AppUpdater) {
const actualEvents: Array<string> = []
for (const eventName of ["checking-for-update", "update-available", "update-downloaded", "error"] as const) {
updater.addListener(eventName, () => {
actualEvents.push(eventName)
})
}
return actualEvents
}