diff --git a/src/main/updater-linux-package-recovery-actions.test.ts b/src/main/updater-linux-package-recovery-actions.test.ts index 4e9d50ecdf5..ff0b974bf7d 100644 --- a/src/main/updater-linux-package-recovery-actions.test.ts +++ b/src/main/updater-linux-package-recovery-actions.test.ts @@ -1,6 +1,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { UpdateStatus } from '../shared/update-status-types' import type * as UpdaterModule from './updater' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { appMock, @@ -95,6 +96,8 @@ const ARTIFACT = { sha512: 'LHlL7dKoqg98gS2nfQv878dK+UoktbAkm4M20/hoJ2Qr0Kqsa3MSL4VmWy/Lll/MYjQFkpvOxduQ/vswentozA==' } +warmUpdaterModule() + describe('linux package recovery actions', () => { afterEach(() => { vi.useRealTimers() @@ -125,7 +128,7 @@ describe('linux package recovery actions', () => { updater: typeof UpdaterModule }> => { const send = vi.fn() - const updater = await import('./updater') + const updater = await loadUpdaterModule() updater.setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) diff --git a/src/main/updater-test-harness.leaked-timers.test.ts b/src/main/updater-test-harness.leaked-timers.test.ts index d40ac5029a9..6857852c0e2 100644 --- a/src/main/updater-test-harness.leaked-timers.test.ts +++ b/src/main/updater-test-harness.leaked-timers.test.ts @@ -1,6 +1,7 @@ import { setTimeout as sleep } from 'node:timers/promises' import { beforeEach, describe, expect, it, vi } from 'vitest' import type { Mock } from 'vitest' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { autoUpdaterMock, fetchNewerReleaseTagsMock, moduleFactories, resetUpdaterMocks } = await vi.hoisted(async () => (await import('./updater-test-harness')).createUpdaterMocks()) @@ -22,6 +23,8 @@ vi.mock('./local-builds/local-build-feed-server', () => moduleFactories.localBui const SILENT_SETTLE_DELAY_MS = 1_000 const AUTO_UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000 +warmUpdaterModule() + describe('updater test harness real-timer tracking', () => { beforeEach(() => { resetUpdaterMocks() @@ -79,7 +82,7 @@ describe('abandoned updater instance', () => { }) leakedStatusSend = vi.fn() - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send: leakedStatusSend } } as never, { getLastUpdateCheckAt: () => Date.now() - 25 * 60 * 60 * 1000 diff --git a/src/main/updater-test-module-loader.test.ts b/src/main/updater-test-module-loader.test.ts new file mode 100644 index 00000000000..0c894ede85b --- /dev/null +++ b/src/main/updater-test-module-loader.test.ts @@ -0,0 +1,32 @@ +import { setTimeout as sleep } from 'node:timers/promises' +import { afterAll, describe, expect, it, vi } from 'vitest' +import { loadUpdaterModule } from './updater-test-module-loader' + +// Why: this pair depends on running in file order — the first test starts an import it never awaits, +// standing in for a test whose `await loadUpdaterModule()` outran `testTimeout`, and the second test +// is the later test the continuation used to land in. +describe('updater module loader', () => { + let outcome: Promise = Promise.resolve('not started') + + afterAll(() => { + vi.doUnmock('./updater') + vi.resetModules() + }) + + it('starts an import that outlives the test that asked for it', () => { + vi.resetModules() + vi.doMock('./updater', async () => { + await sleep(500) + return { setupAutoUpdater: () => {} } + }) + + outcome = loadUpdaterModule().then( + () => 'handed the module over', + (error: Error) => error.message + ) + }) + + it('refuses to hand the module to a test that already ended', async () => { + await expect(outcome).resolves.toContain('resolved after that test ended') + }) +}) diff --git a/src/main/updater-test-module-loader.ts b/src/main/updater-test-module-loader.ts new file mode 100644 index 00000000000..ee0a7355d40 --- /dev/null +++ b/src/main/updater-test-module-loader.ts @@ -0,0 +1,41 @@ +import { beforeAll, TestRunner } from 'vitest' +import type * as UpdaterModule from './updater' + +/** + * Pays `updater.ts`'s transform cost once per file, against `hookTimeout` instead of `testTimeout`. + * + * Why: the module is ~2.4k lines and pulls in a wide graph, so a worker's first import of it costs + * ~1.4s idle but 45s+ on an oversubscribed machine — past the 30s `testTimeout`. `vi.resetModules()` + * re-evaluates the module without re-transforming it, so only a file's *first* import is exposed; + * warming it in a hook moves that one slow import onto the 60s hook budget and leaves every in-test + * import at re-evaluation cost (~25ms idle). + */ +export function warmUpdaterModule(): void { + beforeAll(async () => { + await import('./updater') + }) +} + +/** + * Imports `./updater`, refusing to hand the module to a test that has already ended. + * + * Why: vitest cannot cancel a timed-out test body. When the import outran `testTimeout` the + * continuation went on to call `setupAutoUpdater` during the *next* test, failing it with + * "expected 1 times, but got 2 times" — the exact signature of the abandoned-instance timer flake + * fixed in #17649/#17663, so the timeout read as that regression returning. Throwing here strands the + * continuation and leaves the timeout as the only reported failure. + */ +export async function loadUpdaterModule(): Promise { + const owner = TestRunner.getCurrentTest() + const module = await import('./updater') + if (owner !== undefined && TestRunner.getCurrentTest() !== owner) { + // Why: vitest has already settled the timed-out test's promise, so this throw is swallowed — + // warn separately or the reason the continuation was stranded reaches nobody. + process.emitWarning( + `updater import requested by "${owner.name}" resolved after that test ended. The test timed ` + + `out mid-import; fix that timeout, not the assertions.` + ) + throw new Error(`updater import for "${owner.name}" resolved after that test ended`) + } + return module +} diff --git a/src/main/updater.build-channel-selection.test.ts b/src/main/updater.build-channel-selection.test.ts index 9e036cc2b9e..32253a57b45 100644 --- a/src/main/updater.build-channel-selection.test.ts +++ b/src/main/updater.build-channel-selection.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { appMock, @@ -27,6 +28,8 @@ vi.mock('./local-builds/local-build-feed-server', () => moduleFactories.localBui /** Mirrors AUTO_UPDATE_CHECK_INTERVAL_MS in updater.ts. */ const AUTO_UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000 +warmUpdaterModule() + describe('updater', () => { beforeEach(() => { resetUpdaterMocks() @@ -54,7 +57,7 @@ describe('updater', () => { const platformSpy = vi.spyOn(process, 'platform', 'get').mockReturnValue('linux') try { const send = vi.fn() - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -83,7 +86,7 @@ describe('updater', () => { try { appMock.getVersion.mockReturnValue('1.4.160') const send = vi.fn() - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -113,7 +116,7 @@ describe('updater', () => { try { appMock.getVersion.mockReturnValue('1.4.160-hourly.202607281400') const send = vi.fn() - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -146,7 +149,7 @@ describe('updater', () => { return Promise.resolve(undefined) }) const send = vi.fn() - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -199,7 +202,7 @@ describe('updater', () => { chooseLocalBuildMock.mockRejectedValue(new Error('invalid local build')) const send = vi.fn() const { setupAutoUpdater, checkForUpdates, checkForUpdatesFromMenu } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -242,7 +245,7 @@ describe('updater', () => { }) const send = vi.fn() const { setupAutoUpdater, checkForUpdates, checkForUpdatesFromMenu } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -281,7 +284,7 @@ describe('updater', () => { autoUpdaterMock.checkForUpdates.mockRejectedValueOnce(new Error('local feed failed')) const send = vi.fn() const { setupAutoUpdater, checkForUpdates, checkForUpdatesFromMenu } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -327,7 +330,7 @@ describe('updater', () => { autoUpdaterMock.downloadUpdate.mockRejectedValue(new Error('local download failed')) const send = vi.fn() const { setupAutoUpdater, checkForUpdatesFromMenu, downloadUpdate } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -382,7 +385,7 @@ describe('updater', () => { }) const send = vi.fn() const { setupAutoUpdater, checkForUpdates, checkForUpdatesFromMenu, dismissAvailableUpdate } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -429,7 +432,7 @@ describe('updater', () => { autoUpdaterMock.downloadUpdate.mockResolvedValue(undefined) const send = vi.fn() const { setupAutoUpdater, checkForUpdatesFromMenu, dismissAvailableUpdate, downloadUpdate } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -461,7 +464,7 @@ describe('updater', () => { autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) const mainWindow = { webContents: { send: vi.fn() } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() // Why: recent timestamp defers the startup check so we observe updater state before any RC-mode call, without racing. setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -490,7 +493,7 @@ describe('updater', () => { autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) const mainWindow = { webContents: { send: vi.fn() } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -517,7 +520,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) const setupFeedUrlCalls = autoUpdaterMock.setFeedURL.mock.calls.length @@ -548,7 +551,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -582,7 +585,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu({ includePrerelease: true }) @@ -604,7 +607,7 @@ describe('updater', () => { autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) const mainWindow = { webContents: { send: vi.fn() } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) const initialFeedUrlCalls = autoUpdaterMock.setFeedURL.mock.calls.length diff --git a/src/main/updater.check-failure.test.ts b/src/main/updater.check-failure.test.ts index b0ca31a4556..62d228bf3a7 100644 --- a/src/main/updater.check-failure.test.ts +++ b/src/main/updater.check-failure.test.ts @@ -1,6 +1,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { installNetRequestFetchAdapter } from './updater-net-request.fixture' import { publishingIncident } from './updater-prerelease-feed-reproduction.fixture' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { netFetchMock, netRequestMock } = vi.hoisted(() => ({ netFetchMock: vi.fn(), @@ -157,6 +158,8 @@ function makeBenignCheckFailure(message: string): void { }) } +warmUpdaterModule() + describe('updater check failure handling', () => { beforeEach(() => { vi.resetModules() @@ -188,7 +191,7 @@ describe('updater check failure handling', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -222,7 +225,7 @@ describe('updater check failure handling', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -264,7 +267,7 @@ describe('updater check failure handling', () => { const warnMock = vi.spyOn(console, 'warn').mockImplementation(() => undefined) const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -290,7 +293,7 @@ describe('updater check failure handling', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdates } = await import('./updater') + const { setupAutoUpdater, checkForUpdates } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdates() @@ -322,7 +325,7 @@ describe('updater check failure handling', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdates, getUpdateStatus } = await import('./updater') + const { setupAutoUpdater, checkForUpdates, getUpdateStatus } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdates() @@ -348,7 +351,7 @@ describe('updater check failure handling', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdates } = await import('./updater') + const { setupAutoUpdater, checkForUpdates } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdates() diff --git a/src/main/updater.check-preflight.test.ts b/src/main/updater.check-preflight.test.ts index 2f4e23b4d19..6b2618aad05 100644 --- a/src/main/updater.check-preflight.test.ts +++ b/src/main/updater.check-preflight.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { appMock, @@ -23,6 +24,8 @@ vi.mock('./updater-prerelease-feed', () => moduleFactories.updaterPrereleaseFeed vi.mock('./local-builds/local-build-switch', () => moduleFactories.localBuildSwitch()) vi.mock('./local-builds/local-build-feed-server', () => moduleFactories.localBuildFeedServer()) +warmUpdaterModule() + describe('updater', () => { beforeEach(() => { resetUpdaterMocks() @@ -43,7 +46,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -98,7 +101,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -151,7 +154,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -208,7 +211,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -250,7 +253,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -293,7 +296,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -336,7 +339,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -368,7 +371,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -409,7 +412,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() appMock.getVersion.mockReturnValue('1.4.35') setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null }) @@ -466,7 +469,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null }) checkForUpdatesFromMenu() @@ -515,7 +518,7 @@ describe('updater', () => { autoUpdaterMock.checkForUpdates.mockImplementation(() => new Promise(() => {})) const mainWindow = { webContents: { send: vi.fn() } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() diff --git a/src/main/updater.check-settlement.test.ts b/src/main/updater.check-settlement.test.ts index 61796de8e0f..6513dc40628 100644 --- a/src/main/updater.check-settlement.test.ts +++ b/src/main/updater.check-settlement.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { autoUpdaterMock, @@ -23,6 +24,8 @@ vi.mock('./updater-prerelease-feed', () => moduleFactories.updaterPrereleaseFeed vi.mock('./local-builds/local-build-switch', () => moduleFactories.localBuildSwitch()) vi.mock('./local-builds/local-build-feed-server', () => moduleFactories.localBuildFeedServer()) +warmUpdaterModule() + describe('updater', () => { beforeEach(() => { resetUpdaterMocks() @@ -36,7 +39,7 @@ describe('updater', () => { }) const send = vi.fn() const { setupAutoUpdater, checkForUpdatesFromMenu, dismissAvailableUpdate } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -68,7 +71,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -99,7 +102,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -142,7 +145,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -180,7 +183,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null }) @@ -217,7 +220,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null }) checkForUpdatesFromMenu() @@ -248,7 +251,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null }) @@ -281,7 +284,7 @@ describe('updater', () => { const setLastUpdateCheckAt = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), @@ -308,7 +311,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null, @@ -337,7 +340,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() diff --git a/src/main/updater.headless-serve-install.test.ts b/src/main/updater.headless-serve-install.test.ts index d3cd94f63f8..08e2f519861 100644 --- a/src/main/updater.headless-serve-install.test.ts +++ b/src/main/updater.headless-serve-install.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { appMock, @@ -104,6 +105,8 @@ vi.mock('./serve-update-handoff', () => ({ requestServeUpdateHandoff: requestServeUpdateHandoffMock })) +warmUpdaterModule() + describe('headless serve update install handoff', () => { beforeEach(() => { vi.resetModules() @@ -152,7 +155,7 @@ describe('headless serve update install handoff', () => { }) killAllPtyMock.mockImplementation(beginSessionCleanup) - const { checkForUpdatesFromMenu, quitAndInstall, setupAutoUpdater } = await import('./updater') + const { checkForUpdatesFromMenu, quitAndInstall, setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater( { webContents: { send } } as never, { @@ -226,7 +229,7 @@ describe('headless serve update install handoff', () => { return Promise.resolve(null) }) - const { checkForUpdatesFromMenu, downloadUpdate, setupAutoUpdater } = await import('./updater') + const { checkForUpdatesFromMenu, downloadUpdate, setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now(), installMode: 'unsupported-headless-serve' @@ -283,7 +286,7 @@ describe('headless serve update install handoff', () => { killAllPtyMock.mockImplementation(() => lifecycle.push('in-process-pty-cleanup')) const { checkForUpdatesFromMenu, downloadUpdate, quitAndInstall, setupAutoUpdater } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now(), installMode: 'supervised-headless-serve', @@ -330,7 +333,7 @@ describe('headless serve update install handoff', () => { return Promise.resolve(null) }) - const { checkForUpdatesFromMenu, quitAndInstall, setupAutoUpdater } = await import('./updater') + const { checkForUpdatesFromMenu, quitAndInstall, setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now(), installMode: 'supervised-headless-serve' @@ -368,7 +371,7 @@ describe('headless serve update install handoff', () => { return Promise.resolve(null) }) - const { checkForUpdatesFromMenu, setupAutoUpdater } = await import('./updater') + const { checkForUpdatesFromMenu, setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now(), installMode: 'unsupported-headless-serve' @@ -416,7 +419,7 @@ describe('headless serve update install handoff', () => { return Promise.resolve(null) }) - const { checkForUpdatesFromMenu, setupAutoUpdater } = await import('./updater') + const { checkForUpdatesFromMenu, setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now(), installMode: 'unsupported-headless-serve' @@ -446,7 +449,7 @@ describe('headless serve update install handoff', () => { }) const { checkForUpdatesFromMenu, quitAndInstall, setupAutoUpdater } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now(), installMode: 'unsupported-headless-serve' @@ -481,7 +484,7 @@ describe('headless serve update install handoff', () => { downloadUpdate, getRemoteServerUpdateSupport, setupAutoUpdater - } = await import('./updater') + } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now(), installMode: 'interactive' @@ -507,7 +510,7 @@ describe('headless serve update install handoff', () => { it('advertises remote update control only for safely restartable installs', async () => { const { checkForRemoteServerUpdate, getRemoteServerUpdateSupport, setupAutoUpdater } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater({ webContents: { send: vi.fn() } } as never, { getLastUpdateCheckAt: () => Date.now(), installMode: 'unsupported-headless-serve' diff --git a/src/main/updater.install-failure-cause.test.ts b/src/main/updater.install-failure-cause.test.ts index 5e61ac19554..6344a79d13b 100644 --- a/src/main/updater.install-failure-cause.test.ts +++ b/src/main/updater.install-failure-cause.test.ts @@ -2,6 +2,7 @@ import os from 'node:os' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type * as TracerModule from './observability/tracer' import type * as UpdaterModule from './updater' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { appMock, @@ -145,7 +146,7 @@ async function reachDownloaded(): Promise { // same tracer instance updater.ts will import. tracer = await import('./observability/tracer') tracer.setActiveSink(capturingSink()) - const updater = await import('./updater') + const updater = await loadUpdaterModule() updater.setupAutoUpdater(mainWindow as never) await vi.waitFor(() => { @@ -159,6 +160,8 @@ async function reachDownloaded(): Promise { return updater } +warmUpdaterModule() + /** * On a `.deb` Linux host electron-updater's `install()` catches the failed elevation and * re-dispatches it through the 'error' event *synchronously* inside `quitAndInstall()`. Orca diff --git a/src/main/updater.linux-root-package-install.test.ts b/src/main/updater.linux-root-package-install.test.ts index 8139c5e2102..b52bf40f4c8 100644 --- a/src/main/updater.linux-root-package-install.test.ts +++ b/src/main/updater.linux-root-package-install.test.ts @@ -7,6 +7,7 @@ import type * as UpdaterModule from './updater' import type * as RecoveryModule from './linux-package-update-recovery' import type { UpdateStatus } from '../shared/update-status-types' import { PRE_COMMIT_INSTALL_FAILURE } from './updater-test-harness' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { browserWindowMock, @@ -166,6 +167,8 @@ function probeRevalidation(): RevalidationProbe { } } +warmUpdaterModule() + describe('updater', () => { beforeEach(() => { resetUpdaterMocks() @@ -239,7 +242,7 @@ describe('updater', () => { return Promise.resolve(undefined) }) const send = vi.fn() - const updater = await import('./updater') + const updater = await loadUpdaterModule() updater.setupAutoUpdater({ webContents: { send } } as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -267,7 +270,7 @@ describe('updater', () => { vi.resetModules() autoUpdaterMock.autoInstallOnAppQuit = true getLinuxRootPackageTypeMock.mockReturnValue(packageType) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send: vi.fn() } } as never, { getLastUpdateCheckAt: () => Date.now(), @@ -280,7 +283,7 @@ describe('updater', () => { it('keeps interactive install-on-quit when no root-package marker is present', async () => { autoUpdaterMock.autoInstallOnAppQuit = false - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send: vi.fn() } } as never, { getLastUpdateCheckAt: () => Date.now(), @@ -297,7 +300,7 @@ describe('updater', () => { ] as const) { vi.resetModules() autoUpdaterMock.autoInstallOnAppQuit = true - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater({ webContents: { send: vi.fn() } } as never, { getLastUpdateCheckAt: () => Date.now(), diff --git a/src/main/updater.mac-install.test.ts b/src/main/updater.mac-install.test.ts index f48754afd4f..e4fe26296a0 100644 --- a/src/main/updater.mac-install.test.ts +++ b/src/main/updater.mac-install.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { appMock, @@ -117,6 +118,8 @@ vi.mock('./updater-nudge', () => ({ shouldApplyNudge: vi.fn().mockReturnValue(false) })) +warmUpdaterModule() + describe('updater mac install handoff', () => { beforeEach(() => { vi.resetModules() @@ -142,7 +145,7 @@ describe('updater mac install handoff', () => { const mainWindow = { webContents: { send: sendMock } } autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never) await vi.waitFor(() => { @@ -194,7 +197,7 @@ describe('updater mac install handoff', () => { const mainWindow = { webContents: { send: vi.fn() } } autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) - const { setupAutoUpdater, quitAndInstall } = await import('./updater') + const { setupAutoUpdater, quitAndInstall } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { onBeforeQuit }) await vi.waitFor(() => { @@ -276,7 +279,7 @@ describe('updater mac install handoff', () => { const mainWindow = { webContents: { send: sendMock } } autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never) await vi.waitFor(() => { diff --git a/src/main/updater.nudge-campaign.test.ts b/src/main/updater.nudge-campaign.test.ts index 3ce7a810e5d..eb3faf1eaa2 100644 --- a/src/main/updater.nudge-campaign.test.ts +++ b/src/main/updater.nudge-campaign.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { appMock, @@ -24,6 +25,8 @@ vi.mock('./updater-prerelease-feed', () => moduleFactories.updaterPrereleaseFeed vi.mock('./local-builds/local-build-switch', () => moduleFactories.localBuildSwitch()) vi.mock('./local-builds/local-build-feed-server', () => moduleFactories.localBuildFeedServer()) +warmUpdaterModule() + describe('updater', () => { beforeEach(() => { resetUpdaterMocks() @@ -40,7 +43,7 @@ describe('updater', () => { return Promise.resolve(undefined) }) - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() // Why: recent timestamp defers the startup check so the nudge check runs without hitting the 'checking' guard. setupAutoUpdater(mainWindow as never, { @@ -93,7 +96,7 @@ describe('updater', () => { return Promise.resolve(undefined) }) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null, @@ -127,7 +130,7 @@ describe('updater', () => { return new Promise(() => {}) }) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null, @@ -152,7 +155,7 @@ describe('updater', () => { shouldApplyNudgeMock.mockReturnValue(true) autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never) @@ -188,7 +191,7 @@ describe('updater', () => { return Promise.resolve(undefined) }) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), @@ -226,7 +229,7 @@ describe('updater', () => { fetchNewerReleaseTagsMock.mockResolvedValue({ tags: [], state: 'no-newer' }) autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), @@ -262,7 +265,7 @@ describe('updater', () => { return Promise.resolve(undefined) }) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), @@ -300,7 +303,7 @@ describe('updater', () => { return Promise.reject(missingManifest) }) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), @@ -330,7 +333,7 @@ describe('updater', () => { return Promise.resolve(undefined) }) - const { setupAutoUpdater, dismissNudge } = await import('./updater') + const { setupAutoUpdater, dismissNudge } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), diff --git a/src/main/updater.prerelease-fallback.test.ts b/src/main/updater.prerelease-fallback.test.ts index 3cdf6c0300d..49ed4ca1bb6 100644 --- a/src/main/updater.prerelease-fallback.test.ts +++ b/src/main/updater.prerelease-fallback.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { appMock, autoUpdaterMock, fetchNewerReleaseTagsMock, moduleFactories, resetUpdaterMocks } = await vi.hoisted(async () => (await import('./updater-test-harness')).createUpdaterMocks()) @@ -17,6 +18,8 @@ vi.mock('./updater-prerelease-feed', () => moduleFactories.updaterPrereleaseFeed vi.mock('./local-builds/local-build-switch', () => moduleFactories.localBuildSwitch()) vi.mock('./local-builds/local-build-feed-server', () => moduleFactories.localBuildFeedServer()) +warmUpdaterModule() + describe('updater', () => { beforeEach(() => { resetUpdaterMocks() @@ -45,7 +48,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -89,7 +92,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -133,7 +136,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null }) @@ -181,7 +184,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => lastUpdateCheckAt }) checkForUpdatesFromMenu() @@ -237,7 +240,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -283,7 +286,7 @@ describe('updater', () => { const sendMock = vi.fn() const setLastUpdateCheckAt = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null, @@ -331,7 +334,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -378,7 +381,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null }) @@ -432,7 +435,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -488,7 +491,7 @@ describe('updater', () => { const sendMock = vi.fn() const setLastUpdateCheckAt = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null, @@ -542,7 +545,7 @@ describe('updater', () => { const sendMock = vi.fn() const setLastUpdateCheckAt = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), @@ -597,7 +600,7 @@ describe('updater', () => { const sendMock = vi.fn() const setLastUpdateCheckAt = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), @@ -643,7 +646,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -669,7 +672,7 @@ describe('updater', () => { fetchNewerReleaseTagsMock.mockResolvedValue(['v1.3.18']) autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() const mainWindow = { webContents: { send: vi.fn() } } setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -694,7 +697,7 @@ describe('updater', () => { fetchNewerReleaseTagsMock.mockResolvedValue(['v1.3.18-rc.1']) autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() const mainWindow = { webContents: { send: vi.fn() } } setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) diff --git a/src/main/updater.publishing-window-feed.test.ts b/src/main/updater.publishing-window-feed.test.ts index 37f004aedf9..144eae55845 100644 --- a/src/main/updater.publishing-window-feed.test.ts +++ b/src/main/updater.publishing-window-feed.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { appMock, @@ -24,6 +25,8 @@ vi.mock('./updater-prerelease-feed', () => moduleFactories.updaterPrereleaseFeed vi.mock('./local-builds/local-build-switch', () => moduleFactories.localBuildSwitch()) vi.mock('./local-builds/local-build-feed-server', () => moduleFactories.localBuildFeedServer()) +warmUpdaterModule() + describe('updater', () => { beforeEach(() => { resetUpdaterMocks() @@ -35,7 +38,7 @@ describe('updater', () => { fetchNewerReleaseTagsMock.mockResolvedValue(['v1.3.17-rc.2']) autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() const mainWindow = { webContents: { send: vi.fn() } } setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -67,7 +70,7 @@ describe('updater', () => { fetchNewerReleaseTagsMock.mockResolvedValue(['v1.3.19']) autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() const mainWindow = { webContents: { send: vi.fn() } } setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -89,7 +92,7 @@ describe('updater', () => { fetchNewerReleaseTagsMock.mockResolvedValue([]) autoUpdaterMock.checkForUpdates.mockResolvedValue(undefined) - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() const mainWindow = { webContents: { send: vi.fn() } } setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -115,7 +118,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) const feedCallsBeforeCheck = autoUpdaterMock.setFeedURL.mock.calls.length @@ -146,7 +149,7 @@ describe('updater', () => { }) const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -182,7 +185,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) const feedCallsBeforeCheck = autoUpdaterMock.setFeedURL.mock.calls.length @@ -234,7 +237,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) @@ -273,7 +276,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null, @@ -328,7 +331,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null, @@ -371,7 +374,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null, @@ -444,7 +447,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), @@ -513,7 +516,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), @@ -580,7 +583,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, dismissNudge } = await import('./updater') + const { setupAutoUpdater, dismissNudge } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now(), diff --git a/src/main/updater.quit-and-install.test.ts b/src/main/updater.quit-and-install.test.ts index 945538b3895..0d381ea473a 100644 --- a/src/main/updater.quit-and-install.test.ts +++ b/src/main/updater.quit-and-install.test.ts @@ -1,5 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { PRE_COMMIT_INSTALL_FAILURE } from './updater-test-harness' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { nativeUpdaterMock, @@ -41,6 +42,8 @@ vi.mock('./startup/hydrate-shell-path', () => ({ } })) +warmUpdaterModule() + describe('updater', () => { beforeEach(() => { resetUpdaterMocks() @@ -64,7 +67,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu, downloadUpdate } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu, downloadUpdate } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -102,7 +105,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, checkForUpdatesFromMenu, downloadUpdate } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu, downloadUpdate } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -143,7 +146,7 @@ describe('updater', () => { }) const mainWindow = { webContents: { send: vi.fn() } } - const { setupAutoUpdater, quitAndInstall } = await import('./updater') + const { setupAutoUpdater, quitAndInstall } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never) quitAndInstall() @@ -166,7 +169,7 @@ describe('updater', () => { const onBeforeQuit = vi.fn() const mainWindow = { webContents: { send: vi.fn() } } - const { setupAutoUpdater, quitAndInstall } = await import('./updater') + const { setupAutoUpdater, quitAndInstall } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { onBeforeQuit }) quitAndInstall() @@ -184,7 +187,7 @@ describe('updater', () => { vi.useFakeTimers() const mainWindow = { webContents: { send: vi.fn() } } - const { setupAutoUpdater, quitAndInstall } = await import('./updater') + const { setupAutoUpdater, quitAndInstall } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never) quitAndInstall() @@ -206,7 +209,7 @@ describe('updater', () => { }) ) const mainWindow = { webContents: { send: vi.fn() } } - const { setupAutoUpdater, quitAndInstall } = await import('./updater') + const { setupAutoUpdater, quitAndInstall } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { onBeforeQuit }) quitAndInstall() @@ -237,7 +240,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, quitAndInstall, isQuittingForUpdate } = await import('./updater') + const { setupAutoUpdater, quitAndInstall, isQuittingForUpdate } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never) quitAndInstall() @@ -273,7 +276,7 @@ describe('updater', () => { }) const { setupAutoUpdater, checkForUpdatesFromMenu, quitAndInstall, isQuittingForUpdate } = - await import('./updater') + await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -332,7 +335,7 @@ describe('updater', () => { return Promise.resolve(undefined) }) - const { setupAutoUpdater, checkForUpdatesFromMenu, quitAndInstall } = await import('./updater') + const { setupAutoUpdater, checkForUpdatesFromMenu, quitAndInstall } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() }) checkForUpdatesFromMenu() @@ -377,7 +380,7 @@ describe('updater', () => { }) const mainWindow = { webContents: { send: vi.fn() } } - const { setupAutoUpdater, quitAndInstall, isQuittingForUpdate } = await import('./updater') + const { setupAutoUpdater, quitAndInstall, isQuittingForUpdate } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never) quitAndInstall() @@ -402,7 +405,7 @@ describe('updater', () => { ) const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater, quitAndInstall, isQuittingForUpdate } = await import('./updater') + const { setupAutoUpdater, quitAndInstall, isQuittingForUpdate } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { onBeforeQuit, diff --git a/src/main/updater.startup-scheduling.test.ts b/src/main/updater.startup-scheduling.test.ts index 06a1ea041be..46190de47da 100644 --- a/src/main/updater.startup-scheduling.test.ts +++ b/src/main/updater.startup-scheduling.test.ts @@ -1,4 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-loader' const { appMock, @@ -25,6 +26,8 @@ vi.mock('./updater-prerelease-feed', () => moduleFactories.updaterPrereleaseFeed vi.mock('./local-builds/local-build-switch', () => moduleFactories.localBuildSwitch()) vi.mock('./local-builds/local-build-feed-server', () => moduleFactories.localBuildFeedServer()) +warmUpdaterModule() + describe('updater', () => { beforeEach(() => { resetUpdaterMocks() @@ -34,7 +37,7 @@ describe('updater', () => { isMock.dev = true const mainWindow = { webContents: { send: vi.fn() } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never) @@ -49,7 +52,7 @@ describe('updater', () => { const mainWindow = { webContents: { send: vi.fn() } } const setLastUpdateCheckAt = vi.fn() - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() - 25 * 60 * 60 * 1000, @@ -67,7 +70,7 @@ describe('updater', () => { fetchNudgeMock.mockResolvedValue({ id: 'campaign-1', minVersion: '1.0.0' }) shouldApplyNudgeMock.mockReturnValue(true) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never) @@ -89,7 +92,7 @@ describe('updater', () => { const mainWindow = { webContents: { send: vi.fn() } } const setLastUpdateCheckAt = vi.fn() - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => Date.now() - 23 * 60 * 60 * 1000, @@ -114,7 +117,7 @@ describe('updater', () => { autoUpdaterMock.checkForUpdates.mockImplementation(() => new Promise(() => {})) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => lastUpdateCheckAt @@ -143,7 +146,7 @@ describe('updater', () => { return Promise.reject(new Error('net::ERR_FAILED')) }) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => lastUpdateCheckAt, @@ -178,7 +181,7 @@ describe('updater', () => { const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() setupAutoUpdater(mainWindow as never, { getLastUpdateCheckAt: () => null, @@ -214,7 +217,7 @@ describe('updater', () => { const setLastUpdateCheckAt = vi.fn() const mainWindow = { webContents: { send: sendMock } } - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() // Why: a startup check also arms its own 24h timer, which would fire at the same boundary as the // reschedule under test; entering 23h in makes the startup timer fire the check itself, so only @@ -255,7 +258,7 @@ describe('updater', () => { it('does not disable Windows Authenticode verification on win32', async () => { vi.stubGlobal('process', { ...process, platform: 'win32' }) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } } @@ -268,7 +271,7 @@ describe('updater', () => { it('does not override verifyUpdateCodeSignature on non-Windows platforms', async () => { vi.stubGlobal('process', { ...process, platform: 'darwin' }) - const { setupAutoUpdater } = await import('./updater') + const { setupAutoUpdater } = await loadUpdaterModule() const sendMock = vi.fn() const mainWindow = { webContents: { send: sendMock } }