import { BitbucketOptions, GenericServerOptions, GithubOptions, KeygenOptions, S3Options, SpacesOptions } from "builder-util-runtime" import { BitbucketPublisher } from "electron-publish" import { UpdateCheckResult } from "electron-updater" import { outputFile } from "fs-extra" import { tmpdir } from "os" import * as path from "path" import { assertThat } from "../helpers/fileAssert" import { removeUnstableProperties } from "../helpers/packTester" import { createNsisUpdater, trackEvents, validateDownload, writeUpdateConfig } from "../helpers/updaterTestUtil" import { ExpectStatic } from "vitest" const config = { retry: 3 } test("downgrade (disallowed, beta)", config, async ({ expect }) => { const updater = await createNsisUpdater("1.5.2-beta.4") updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "develar", repo: "__test_nsis_release", }) const actualEvents: Array = [] const expectedEvents = ["checking-for-update", "update-not-available"] as const for (const eventName of expectedEvents) { updater.addListener(eventName, () => { actualEvents.push(eventName) }) } const updateCheckResult = await updater.checkForUpdates() expect(removeUnstableProperties(updateCheckResult?.updateInfo)).toMatchSnapshot() // noinspection JSIgnoredPromiseFromCall expect(updateCheckResult?.downloadPromise).toBeUndefined() expect(actualEvents).toEqual(expectedEvents) }) test("github allowPrerelease=true", config, async ({ expect }) => { const updater = await createNsisUpdater("1.0.1") updater.allowPrerelease = true updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "mmaietta", repo: "electron-builder-test-prerelease", }) const updateCheckResult = await updater.checkForUpdates() expect(removeUnstableProperties(updateCheckResult?.updateInfo)).toMatchSnapshot() }) test("github allowPrerelease=false", config, async ({ expect }) => { const updater = await createNsisUpdater("1.0.1") updater.allowPrerelease = false updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "mmaietta", repo: "electron-builder-test-prerelease", }) const updateCheckResult = await updater.checkForUpdates() expect(removeUnstableProperties(updateCheckResult?.updateInfo)).toMatchSnapshot() }) test("file url generic", config, async ({ expect }) => { const updater = await createNsisUpdater() updater.updateConfigPath = await writeUpdateConfig({ provider: "generic", url: "https://develar.s3.amazonaws.com/test", }) await validateDownload(expect, updater) }) test.ifEnv(process.env.KEYGEN_TOKEN)("file url keygen", config, async ({ expect }) => { const updater = await createNsisUpdater() updater.addAuthHeader(`Bearer ${process.env.KEYGEN_TOKEN}`) updater.updateConfigPath = await writeUpdateConfig({ provider: "keygen", product: process.env.KEYGEN_PRODUCT || "43981278-96e7-47de-b8c2-98d59987206b", account: process.env.KEYGEN_ACCOUNT || "cdecda36-3ef0-483e-ad88-97e7970f3149", }) await validateDownload(expect, updater) }) test.ifEnv(process.env.BITBUCKET_TOKEN)("file url bitbucket", config, async ({ expect }) => { const updater = await createNsisUpdater() const options: BitbucketOptions = { provider: "bitbucket", owner: "mike-m", slug: "electron-builder-test", } updater.addAuthHeader(BitbucketPublisher.convertAppPassword(options.owner, process.env.BITBUCKET_TOKEN!)) updater.updateConfigPath = await writeUpdateConfig(options) await validateDownload(expect, updater) }) test.skip("DigitalOcean Spaces", config, async ({ expect }) => { const updater = await createNsisUpdater() updater.updateConfigPath = await writeUpdateConfig({ provider: "spaces", name: "electron-builder-test", path: "light-updater-test", region: "nyc3", }) await validateDownload(expect, updater) }) test.ifNotCiWin.skip("sha512 mismatch error event", config, async ({ expect }) => { const updater = await createNsisUpdater() updater.updateConfigPath = await writeUpdateConfig({ provider: "generic", url: "https://develar.s3.amazonaws.com/test", channel: "beta", }) const actualEvents = trackEvents(updater) const updateCheckResult = await updater.checkForUpdates() expect(removeUnstableProperties(updateCheckResult?.updateInfo)).toMatchSnapshot() await assertThat(expect, updateCheckResult?.downloadPromise).throws() expect(actualEvents).toMatchSnapshot() }) test("file url generic - manual download", config, async ({ expect }) => { const updater = await createNsisUpdater() updater.updateConfigPath = await writeUpdateConfig({ provider: "generic", url: "https://develar.s3.amazonaws.com/test", }) updater.autoDownload = false const actualEvents = trackEvents(updater) const updateCheckResult = await updater.checkForUpdates() expect(removeUnstableProperties(updateCheckResult?.updateInfo)).toMatchSnapshot() // noinspection JSIgnoredPromiseFromCall expect(updateCheckResult?.downloadPromise).toBeNull() expect(actualEvents).toMatchSnapshot() await assertThat(expect, path.join((await updater.downloadUpdate())[0])).isFile() }) // https://github.com/electron-userland/electron-builder/issues/1045 test("checkForUpdates several times", config, async ({ expect }) => { const updater = await createNsisUpdater() updater.updateConfigPath = await writeUpdateConfig({ provider: "generic", url: "https://develar.s3.amazonaws.com/test", }) const actualEvents = trackEvents(updater) for (let i = 0; i < 10; i++) { //noinspection JSIgnoredPromiseFromCall void updater.checkForUpdates() } async function checkForUpdates() { const updateCheckResult = await updater.checkForUpdates() expect(removeUnstableProperties(updateCheckResult?.updateInfo)).toMatchSnapshot() await checkDownloadPromise(expect, updateCheckResult) } await checkForUpdates() // we must not download the same file again await checkForUpdates() expect(actualEvents).toMatchSnapshot() }) async function checkDownloadPromise(expect: ExpectStatic, updateCheckResult: UpdateCheckResult | null) { return await assertThat(expect, path.join((await updateCheckResult?.downloadPromise)![0])).isFile() } test("file url github", config, async ({ expect }) => { const updater = await createNsisUpdater() const options: GithubOptions = { provider: "github", owner: "develar", repo: "__test_nsis_release", } updater.updateConfigPath = await writeUpdateConfig(options) updater.signals.updateDownloaded(info => { expect(info.downloadedFile).not.toBeNull() delete (info as any).downloadedFile expect(info).toMatchSnapshot() }) await validateDownload(expect, updater) }) test("file url github pre-release and fullChangelog", config, async ({ expect }) => { const updater = await createNsisUpdater("1.5.0-beta.1") const options: GithubOptions = { provider: "github", owner: "develar", repo: "__test_nsis_release", } updater.fullChangelog = true updater.updateConfigPath = await writeUpdateConfig(options) updater.signals.updateDownloaded(info => { expect(info.downloadedFile).not.toBeNull() delete (info as any).downloadedFile expect(info).toMatchSnapshot() }) const updateCheckResult = await validateDownload(expect, updater) expect(updateCheckResult?.updateInfo).toMatchSnapshot() }) test.ifEnv(process.env.GH_TOKEN || process.env.GITHUB_TOKEN)("file url github private", config, async ({ expect }) => { const updater = await createNsisUpdater("0.0.1") updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "develar", repo: "__test_nsis_release_private", private: true, }) await validateDownload(expect, updater) }) test("test error", config, async ({ expect }) => { const updater = await createNsisUpdater("0.0.1") const actualEvents = trackEvents(updater) await assertThat(expect, updater.checkForUpdates()).throws() expect(actualEvents).toMatchSnapshot() }) test.skip("test download progress", config, async ({ expect }) => { const updater = await createNsisUpdater("0.0.1") updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "develar", repo: "__test_nsis_release", }) updater.autoDownload = false const progressEvents: Array = [] updater.signals.progress(it => progressEvents.push(it)) await updater.checkForUpdates() await updater.downloadUpdate() expect(progressEvents.length).toBeGreaterThanOrEqual(1) const lastEvent = progressEvents.pop() expect(lastEvent.percent).toBe(100) expect(lastEvent.bytesPerSecond).toBeGreaterThan(1) expect(lastEvent.transferred).toBe(lastEvent.total) }) test("valid signature", config, async ({ expect }) => { const updater = await createNsisUpdater("0.0.1") updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "develar", repo: "__test_nsis_release", publisherName: ["Vladimir Krivosheev"], }) await validateDownload(expect, updater) }) test("valid signature - multiple publisher DNs", config, async ({ expect }) => { const updater = await createNsisUpdater("0.0.1") updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "develar", repo: "__test_nsis_release", publisherName: ["Foo Bar", "CN=Vladimir Krivosheev, O=Vladimir Krivosheev, L=Grunwald, S=Bayern, C=DE", "Bar Foo"], }) await validateDownload(expect, updater) }) test("valid signature using DN", config, async ({ expect }) => { const updater = await createNsisUpdater("0.0.1") updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "develar", repo: "__test_nsis_release", publisherName: ["CN=Vladimir Krivosheev, O=Vladimir Krivosheev, L=Grunwald, S=Bayern, C=DE"], }) await validateDownload(expect, updater) }) test.ifWindows("invalid signature", config, async ({ expect }) => { const updater = await createNsisUpdater("0.0.1") updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "develar", repo: "__test_nsis_release", publisherName: ["Foo Bar"], }) const actualEvents = trackEvents(updater) await assertThat( expect, updater.checkForUpdates().then((it): any => it?.downloadPromise) ).throws() expect(actualEvents).toMatchSnapshot() }) test.ifWindows("test custom signature verifier", config, async ({ expect }) => { const updater = await createNsisUpdater("1.0.2") updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "develar", repo: "__test_nsis_release", publisherName: ["CN=Vladimir Krivosheev, O=Vladimir Krivosheev, L=Grunwald, S=Bayern, C=DE"], }) updater.verifyUpdateCodeSignature = (publisherName: string[], path: string) => { return Promise.resolve(null) } await validateDownload(expect, updater) }) test.ifWindows("test custom signature verifier - signing error message", config, async ({ expect }) => { const updater = await createNsisUpdater("1.0.2") updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "develar", repo: "__test_nsis_release", publisherName: ["CN=Vladimir Krivosheev, O=Vladimir Krivosheev, L=Grunwald, S=Bayern, C=DE"], }) updater.verifyUpdateCodeSignature = (publisherName: string[], path: string) => { return Promise.resolve("signature verification failed") } const actualEvents = trackEvents(updater) await assertThat( expect, updater.checkForUpdates().then((it): any => it?.downloadPromise) ).throws() expect(actualEvents).toMatchSnapshot() }) // disable for now test("90 staging percentage", config, async ({ expect }) => { const userIdFile = path.join(tmpdir(), "electron-updater-test", "userData", ".updaterId") await outputFile(userIdFile, "1wa70172-80f8-5cc4-8131-28f5e0edd2a1") const updater = await createNsisUpdater("0.0.1") updater.updateConfigPath = await writeUpdateConfig({ provider: "s3", channel: "staging-percentage", bucket: "develar", path: "test", }) await validateDownload(expect, updater) }) test("1 staging percentage", config, async ({ expect }) => { const userIdFile = path.join(tmpdir(), "electron-updater-test", "userData", ".updaterId") await outputFile(userIdFile, "12a70172-80f8-5cc4-8131-28f5e0edd2a1") const updater = await createNsisUpdater("0.0.1") updater.updateConfigPath = await writeUpdateConfig({ provider: "s3", channel: "staging-percentage-small", bucket: "develar", path: "test", }) await validateDownload(expect, updater, false) }) test("cancel download with progress", config, async ({ expect }) => { const updater = await createNsisUpdater() updater.updateConfigPath = await writeUpdateConfig({ provider: "generic", url: "https://develar.s3.amazonaws.com/full-test", }) const progressEvents: Array = [] updater.signals.progress(it => progressEvents.push(it)) let cancelled = false updater.signals.updateCancelled(() => (cancelled = true)) const checkResult = await updater.checkForUpdates() checkResult?.cancellationToken!.cancel() if (progressEvents.length > 0) { const lastEvent = progressEvents[progressEvents.length - 1] expect(lastEvent.percent).not.toBe(100) expect(lastEvent.bytesPerSecond).toBeGreaterThan(1) expect(lastEvent.transferred).not.toBe(lastEvent.total) } const downloadPromise = checkResult?.downloadPromise await assertThat(expect, downloadPromise).throws() expect(cancelled).toBe(true) }) test("test download and install", config, async ({ expect }) => { const updater = await createNsisUpdater() updater.updateConfigPath = await writeUpdateConfig({ provider: "generic", url: "https://develar.s3.amazonaws.com/test", }) await validateDownload(expect, updater) }) test.ifWindows.skip("test downloaded installer", config, async ({ expect }) => { const updater = await createNsisUpdater("1.0.1") updater.updateConfigPath = await writeUpdateConfig({ provider: "github", owner: "mmaietta", repo: "electron-builder-test", }) const actualEvents = trackEvents(updater) await validateDownload(expect, updater) // expect(actualEvents).toMatchObject(["checking-for-update", "update-available", "update-downloaded"]) updater.quitAndInstall(true, false) expect(actualEvents).toMatchObject(["checking-for-update", "update-available", "update-downloaded", "before-quit-for-update"]) })