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 } // to reduce difference in test mode, setFeedURL is not used to set (NsisUpdater also read configOnDisk to load original publisherName) export async function writeUpdateConfig(data: T): Promise { 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) { // noinspection JSIgnoredPromiseFromCall 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 { // noinspection JSIgnoredPromiseFromCall expect(updateCheckResult?.downloadPromise).toBeUndefined() } expect(actualEvents).toMatchSnapshot() return updateCheckResult } export class TestNodeHttpExecutor extends NodeHttpExecutor { async download(url: string, destination: string, options: DownloadOptions): Promise { 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 = [] for (const eventName of ["checking-for-update", "update-available", "update-downloaded", "error"] as const) { updater.addListener(eventName, () => { actualEvents.push(eventName) }) } return actualEvents }