import { assert, describe, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { beforeEach, vi } from "vite-plus/test"; const { autoUpdaterMock } = vi.hoisted(() => ({ autoUpdaterMock: { allowDowngrade: false, allowPrerelease: false, autoDownload: true, autoInstallOnAppQuit: true, channel: "latest", disableDifferentialDownload: false, checkForUpdates: vi.fn(() => Promise.resolve(null)), downloadUpdate: vi.fn(() => Promise.resolve([])), on: vi.fn(), quitAndInstall: vi.fn(), removeListener: vi.fn(), setFeedURL: vi.fn(), }, })); vi.mock("electron-updater", () => ({ autoUpdater: autoUpdaterMock, })); import * as ElectronUpdater from "./ElectronUpdater.ts"; describe("ElectronUpdater", () => { beforeEach(() => { autoUpdaterMock.allowDowngrade = false; autoUpdaterMock.allowPrerelease = false; autoUpdaterMock.autoDownload = true; autoUpdaterMock.autoInstallOnAppQuit = true; autoUpdaterMock.channel = "latest"; autoUpdaterMock.disableDifferentialDownload = false; autoUpdaterMock.checkForUpdates.mockClear(); autoUpdaterMock.checkForUpdates.mockImplementation(() => Promise.resolve(null)); autoUpdaterMock.downloadUpdate.mockClear(); autoUpdaterMock.downloadUpdate.mockImplementation(() => Promise.resolve([])); autoUpdaterMock.on.mockClear(); autoUpdaterMock.quitAndInstall.mockClear(); autoUpdaterMock.removeListener.mockClear(); autoUpdaterMock.setFeedURL.mockClear(); }); it.effect("scopes updater event listeners", () => Effect.gen(function* () { const listener = vi.fn(); yield* Effect.scoped( Effect.gen(function* () { const updater = yield* ElectronUpdater.ElectronUpdater; yield* updater.on("update-available", listener); }), ); assert.deepEqual(autoUpdaterMock.on.mock.calls, [["update-available", listener]]); assert.deepEqual(autoUpdaterMock.removeListener.mock.calls, [["update-available", listener]]); }).pipe(Effect.provide(ElectronUpdater.layer)), ); it.effect("wraps rejected update checks in the method-specific typed error", () => Effect.gen(function* () { const cause = new Error("network unavailable"); autoUpdaterMock.checkForUpdates.mockImplementationOnce(() => Promise.reject(cause)); const updater = yield* ElectronUpdater.ElectronUpdater; autoUpdaterMock.channel = "beta"; const error = yield* updater.checkForUpdates.pipe(Effect.flip); assert.instanceOf(error, ElectronUpdater.ElectronUpdaterCheckForUpdatesError); assert.isTrue(ElectronUpdater.isElectronUpdaterError(error)); assert.equal(error.channel, "beta"); assert.strictEqual(error.cause, cause); assert.equal(error.message, "Electron updater failed to check for updates on channel beta."); assert.notInclude(error.message, cause.message); }).pipe(Effect.provide(ElectronUpdater.layer)), ); it.effect("preserves the execution-time channel on download failures", () => Effect.gen(function* () { const cause = new Error("download unavailable"); autoUpdaterMock.downloadUpdate.mockImplementationOnce(() => Promise.reject(cause)); const updater = yield* ElectronUpdater.ElectronUpdater; autoUpdaterMock.channel = "nightly"; const error = yield* updater.downloadUpdate.pipe(Effect.flip); assert.instanceOf(error, ElectronUpdater.ElectronUpdaterDownloadUpdateError); assert.isTrue(ElectronUpdater.isElectronUpdaterError(error)); assert.equal(error.channel, "nightly"); assert.strictEqual(error.cause, cause); assert.equal( error.message, "Electron updater failed to download the update on channel nightly.", ); assert.notInclude(error.message, cause.message); }).pipe(Effect.provide(ElectronUpdater.layer)), ); it.effect("preserves quit-and-install flags and the execution-time channel", () => Effect.gen(function* () { const cause = new Error("quit and install failed"); autoUpdaterMock.quitAndInstall.mockImplementationOnce(() => { throw cause; }); const updater = yield* ElectronUpdater.ElectronUpdater; autoUpdaterMock.channel = "alpha"; const error = yield* updater .quitAndInstall({ isSilent: true, isForceRunAfter: false }) .pipe(Effect.flip); assert.instanceOf(error, ElectronUpdater.ElectronUpdaterQuitAndInstallError); assert.isTrue(ElectronUpdater.isElectronUpdaterError(error)); assert.equal(error.channel, "alpha"); assert.equal(error.isSilent, true); assert.equal(error.isForceRunAfter, false); assert.strictEqual(error.cause, cause); assert.equal( error.message, "Electron updater failed to quit and install the update on channel alpha (silent: true, force run after: false).", ); assert.notInclude(error.message, cause.message); assert.deepEqual(autoUpdaterMock.quitAndInstall.mock.calls, [[true, false]]); }).pipe(Effect.provide(ElectronUpdater.layer)), ); });