chore: replace electron-toolkit utilities with Electron APIs (#31824)

* chore: replace electron-toolkit utilities with Electron APIs

* refactor: reuse development flag within startup preflight
This commit is contained in:
Neil
2026-10-21 21:52:08 -07:00
committed by GitHub
parent b0778082cb
commit 35596d16a5
63 changed files with 112 additions and 211 deletions
-1
View File
@@ -21,7 +21,6 @@ const requireFromProject = createRequire(join(projectDir, 'package.json'))
const PACKAGED_RUNTIME_PACKAGE_ROOTS = [
'@anthropic-ai/claude-agent-sdk',
'@electron-toolkit/utils',
'@linear/sdk',
'@orca/process-host',
'@parcel/watcher',
-1
View File
@@ -196,7 +196,6 @@
},
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "0.3.284",
"@electron-toolkit/utils": "^4.0.0",
"@floating-ui/dom": "1.8.0",
"@linear/sdk": "^97.0.0",
"@orca/process-host": "workspace:*",
-16
View File
@@ -195,9 +195,6 @@ importers:
'@anthropic-ai/claude-agent-sdk':
specifier: 0.3.284
version: 0.3.284(@anthropic-ai/sdk@0.122.0(zod@4.6.7))(@modelcontextprotocol/sdk@1.32.1(supports-color@7.2.0)(zod@4.6.7))(zod@4.6.7)
'@electron-toolkit/utils':
specifier: ^4.0.0
version: 4.0.0(electron@43.7.8(supports-color@7.2.0))
'@floating-ui/dom':
specifier: 1.8.0
version: 1.8.0
@@ -889,15 +886,6 @@ packages:
peerDependencies:
'@types/node': '*'
'@electron-toolkit/utils@4.0.0':
resolution: {integrity: sha512-qXSntwEzluSzKl4z5yFNBknmPGjPa3zFhE4mp9+h0cgokY5ornAeP+CJQDBhKsL1S58aOQfcwkD3NwLZCl+64g==}
peerDependencies:
'@types/electron': '*'
electron: '>=13.0.0'
peerDependenciesMeta:
'@types/electron':
optional: true
'@electron/asar@3.4.1':
resolution: {integrity: sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==}
engines: {node: '>=10.12.0'}
@@ -8265,10 +8253,6 @@ snapshots:
dependencies:
'@types/node': 24.19.2
'@electron-toolkit/utils@4.0.0(electron@43.7.8(supports-color@7.2.0))':
dependencies:
electron: 43.7.8(supports-color@7.2.0)
'@electron/asar@3.4.1':
dependencies:
commander: 5.1.0
+8 -19
View File
@@ -1,30 +1,20 @@
import { EventEmitter } from 'node:events'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const {
browserWindowGetAllWindowsMock,
createFromPathMock,
dockSetIconMock,
isMock,
windowSetIconMock
} = vi.hoisted(() => ({
browserWindowGetAllWindowsMock: vi.fn(),
createFromPathMock: vi.fn(),
dockSetIconMock: vi.fn(),
isMock: { dev: false },
windowSetIconMock: vi.fn()
}))
const { browserWindowGetAllWindowsMock, createFromPathMock, dockSetIconMock, windowSetIconMock } =
vi.hoisted(() => ({
browserWindowGetAllWindowsMock: vi.fn(),
createFromPathMock: vi.fn(),
dockSetIconMock: vi.fn(),
windowSetIconMock: vi.fn()
}))
vi.mock('electron', () => ({
app: { dock: { setIcon: dockSetIconMock } },
app: { getPath: () => '/test/electron', isPackaged: true, dock: { setIcon: dockSetIconMock } },
BrowserWindow: { getAllWindows: browserWindowGetAllWindowsMock },
nativeImage: { createFromPath: createFromPathMock }
}))
vi.mock('@electron-toolkit/utils', () => ({
is: isMock
}))
vi.mock('../../resources/icon.png?asset', () => ({
default: 'classic-icon'
}))
@@ -75,7 +65,6 @@ describe('app icon selection', () => {
createFromPathMock.mockReset()
dockSetIconMock.mockReset()
windowSetIconMock.mockReset()
isMock.dev = false
})
afterEach(() => {
+2 -3
View File
@@ -5,7 +5,6 @@ import {
} from 'node:child_process'
import { dirname, resolve } from 'node:path'
import { app, BrowserWindow, nativeImage } from 'electron'
import { is } from '@electron-toolkit/utils'
import classicIcon from '../../resources/icon.png?asset'
import classicDevIcon from '../../resources/icon-dev.png?asset'
import watercolorIcon from '../../resources/app-icons/orca-watercolor.png?asset'
@@ -15,7 +14,7 @@ import blueMacDockIcon from '../../resources/app-icons/orca-blue.png?asset&asarU
import { normalizeAppIconId, type AppIconId } from '../shared/app-icon'
const APP_ICON_PATHS = {
classic: is.dev ? classicDevIcon : classicIcon,
classic: !app.isPackaged ? classicDevIcon : classicIcon,
watercolor: watercolorIcon,
blue: blueIcon
} satisfies Record<AppIconId, string>
@@ -261,7 +260,7 @@ function clearMacCustomIconMetadata(execFile: ExecFile, appBundlePath: string):
export function persistMacDockIcon(value: unknown, options: PersistMacDockIconOptions = {}): void {
const platform = options.platform ?? process.platform
const isDevApp = options.isDevApp ?? (is.dev || !app.isPackaged)
const isDevApp = options.isDevApp ?? !app.isPackaged
if (platform !== 'darwin' || isDevApp) {
return
}
-4
View File
@@ -101,10 +101,6 @@ vi.mock('electron', () => ({
}
}))
vi.mock('@electron-toolkit/utils', () => ({
is: { dev: true }
}))
vi.mock('../tray/system-tray', () => ({
destroySystemTray: destroySystemTrayMock,
rebuildSystemTrayMenu: rebuildSystemTrayMenuMock
+1 -2
View File
@@ -5,7 +5,6 @@ import { existsSync } from 'node:fs'
import path from 'node:path'
import { pathToFileURL } from 'node:url'
import { app, BrowserWindow, dialog, ipcMain, type IpcMainInvokeEvent } from 'electron'
import { is } from '@electron-toolkit/utils'
import type { AppIdentity } from '../../shared/app-identity'
import type { MarkdownDocument } from '../../shared/filesystem-entry-types'
import type { FloatingTerminalCwdRequest } from '../../shared/ui-chrome-types'
@@ -150,7 +149,7 @@ export function registerAppHandlers(store: Store, options: RegisterAppHandlersOp
ipcMain.handle('app:getFeatureWallAssetBaseUrl', (): string => getFeatureWallAssetBaseUrl())
ipcMain.handle('app:getIdentity', (): AppIdentity => {
const identity = getDevInstanceIdentity(is.dev)
const identity = getDevInstanceIdentity(!app.isPackaged)
return {
name: identity.name,
isDev: identity.isDev,
@@ -22,12 +22,6 @@ vi.mock('../../../../../git/worktree', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
listWorktreesStrict
}))
// The push path reaches the dashboard popout window, whose electron re-export cannot load here.
vi.mock('@electron-toolkit/utils', () => ({
is: { dev: false },
optimizer: { watchWindowShortcuts: vi.fn() },
electronApp: { setAppUserModelId: vi.fn() }
}))
import type Database from '../../../../../sqlite/sync-database'
import type { AgentStatusIpcPayload } from '../../../../../../shared/agent-status-ipc-payload'
@@ -59,7 +59,6 @@ vi.mock('electron', () => ({
vi.mock('../window/foreground-activation-policy', () => ({
isBackgroundLaunch: mocks.backgroundLaunch
}))
vi.mock('@electron-toolkit/utils', () => ({ is: { dev: true } }))
vi.mock('./cli-launch-redirect', () => ({
maybeRedirectCliLaunch: () => ({ redirected: false, status: 0 })
}))
@@ -13,7 +13,6 @@ const fixture = vi.hoisted(() => {
})
vi.mock('electron', () => ({ app: fixture.app, nativeTheme: {} }))
vi.mock('@electron-toolkit/utils', () => ({ is: { dev: false } }))
vi.mock('../codex/codex-hook-reconcile', () => ({ startCodexHooks: fixture.startCodexHooks }))
vi.mock('../agent-hooks/local-agent-cli-presence', () => ({
hydrateAgentCliShellPath: () => fixture.pathReady
@@ -9,10 +9,10 @@ const { showMessageBox, userData } = vi.hoisted(() => ({
userData: { path: '' }
}))
// Why the mocks: gpu-lifecycle's import graph reaches electron and the toolkit's
// electron re-export. Everything below this is the real module under test.
// Why: only Electron is mocked; GPU lifecycle decisions stay real.
vi.mock('electron', () => ({
app: {
isPackaged: true,
getPath: () => userData.path,
getVersion: () => '1.4.184',
getGPUFeatureStatus: () => ({}),
@@ -26,11 +26,6 @@ vi.mock('electron', () => ({
},
dialog: { showMessageBox }
}))
vi.mock('@electron-toolkit/utils', () => ({
is: { dev: false },
optimizer: { watchWindowShortcuts: vi.fn() },
electronApp: { setAppUserModelId: vi.fn() }
}))
import { grantsFrom } from './windows-install-dir-acl.test-fixture'
import {
+7 -7
View File
@@ -1,7 +1,6 @@
import { antigravityHookService } from '../antigravity/hook-service'
import { getRelocatedDaemonHost } from '../daemon/daemon-host-relocation'
import { app, ipcMain, powerMonitor, session } from 'electron'
import { is } from '@electron-toolkit/utils'
import os from 'node:os'
import { join } from 'node:path'
import { maybeRedirectCliLaunch } from './cli-launch-redirect'
@@ -130,6 +129,7 @@ export function runMainProcessPreflight(options: MainProcessPreflightOptions): b
}
function initializeMainProcessPreflight(options: MainProcessPreflightOptions): boolean {
const isDev = !app.isPackaged
// Why first: every step below, recovery and the instance lock included, may resolve userData.
ensureWindowsAppDataPath(app)
if (runProfileStateRecoveryPreflight()) {
@@ -161,7 +161,7 @@ function initializeMainProcessPreflight(options: MainProcessPreflightOptions): b
if (state.isServeMode) {
reserveServeStdoutForReadiness()
}
state.devInstanceIdentity = getDevInstanceIdentity(is.dev)
state.devInstanceIdentity = getDevInstanceIdentity(isDev)
state.devAgentHookEndpointNamespace = state.devInstanceIdentity.isDev
? state.devInstanceIdentity.appUserModelId
: undefined
@@ -208,7 +208,7 @@ function initializeMainProcessPreflight(options: MainProcessPreflightOptions): b
// Why before any spawn: `signalProcessTree` is shared with the CLI and relay, so
// it can only reach the main-process guard and breadcrumb store once this is registered.
installMainProcessTreeKillGate()
configureDevUserDataPath(is.dev)
configureDevUserDataPath(isDev)
configureOrcaUserDataPathEnv()
// Why these four lines are one step (#16761): the two above decide where userData lives, and
// everything below may resolve a path. Installing the accessor any later leaves a window where an
@@ -253,8 +253,8 @@ function initializeMainProcessPreflight(options: MainProcessPreflightOptions): b
startMainThreadChurnProbe({ extraStats: () => ({ diffCache: settledDiffCache.stats() }) })
// Why: acquire AFTER configureDevUserDataPath — Electron derives lock identity from `userData`, so dev/packaged lock in separate namespaces.
// Why dev locks too: two processes on one profile corrupt its stores (PR #1326 / #1312); parallel `pnpm dev` needs ORCA_DEV_USER_DATA_PATH per copy.
const bypass = shouldBypassSingleInstanceLock({ isDev: is.dev, isServeMode: state.isServeMode })
const skip = shouldSkipSingleInstanceLock({ isDev: is.dev, isServeMode: state.isServeMode })
const bypass = shouldBypassSingleInstanceLock({ isDev, isServeMode: state.isServeMode })
const skip = shouldSkipSingleInstanceLock({ isDev, isServeMode: state.isServeMode })
if (bypass) {
// Why: diagnostic escape hatch for macOS builds where Electron reports a false lock loss before any app logs exist.
logSingleInstanceLockBypass()
@@ -271,7 +271,7 @@ function initializeMainProcessPreflight(options: MainProcessPreflightOptions): b
// Why: a false-negative lock loss otherwise looks like a silent crash on packaged macOS; `open --stderr` can capture this line.
// In dev it is the line `pnpm dev` prints before exiting.
logSingleInstanceLockFailure({
isDevDesktop: is.dev && !state.isServeMode,
isDevDesktop: isDev && !state.isServeMode,
userDataPath: app.getPath('userData')
})
// Why: a graceful quit is deferred pre-ready, so this launch would still walk into Linux display init and SIGSEGV (#11935).
@@ -325,7 +325,7 @@ function initializeMainProcessPreflight(options: MainProcessPreflightOptions): b
setSpeechServiceFactories(electronSpeechServiceFactories)
setWorktreeWatcherRemoval(desktopWorktreeWatcherRemoval)
// Why: couple to dev-parent only for electron-vite desktop runs; `orca serve`'s parent (CLI shim/background shell) isn't the intended server lifetime.
const shouldCoupleToDevParent = is.dev && !state.isServeMode
const shouldCoupleToDevParent = isDev && !state.isServeMode
installDevParentDisconnectQuit(shouldCoupleToDevParent)
installDevParentWatchdog(shouldCoupleToDevParent)
installDevParentSignalQuit(shouldCoupleToDevParent)
@@ -39,7 +39,6 @@ vi.mock('electron', () => ({
app: { isPackaged: false, on: vi.fn(), once: vi.fn(), getPath: () => fixture.userData },
nativeTheme: {}
}))
vi.mock('@electron-toolkit/utils', () => ({ is: { dev: false } }))
vi.mock('./main-process-state', () => ({ mainProcessState: fixture.state }))
vi.mock('../runtime/orca-runtime', () => ({
OrcaRuntimeService: class {
@@ -1,5 +1,4 @@
import { app, session } from 'electron'
import { electronApp, is } from '@electron-toolkit/utils'
import { applyBackgroundActivationPolicy } from '../window/foreground-activation-policy'
import { applyElectronProxySettings } from '../network/proxy-settings'
import { installElectronProxyRequestGuard } from '../network/electron-proxy-request-guard'
@@ -97,7 +96,9 @@ export async function initializeReadyFoundation(): Promise<void> {
if (!identity) {
throw new Error('Development identity is unavailable')
}
electronApp.setAppUserModelId(identity.appUserModelId)
if (process.platform === 'win32') {
app.setAppUserModelId(app.isPackaged ? identity.appUserModelId : process.execPath)
}
// Why: names the app menu/About panel. Dev already applied this pre-ready (see the
// safeStorage note above); this call stays unconditional so packaged builds keep their
// existing post-ready rename, which lands after the Keychain name is already resolved.
@@ -184,7 +185,7 @@ export async function initializeReadyFoundation(): Promise<void> {
dataFile: profile.dataFile,
force: process.env.ORCA_ALWAYS_REPORT_SECRET_PROTECTION === '1',
deferUntilFirstWindow: !state.isServeMode,
skipInDevelopment: is.dev
skipInDevelopment: !app.isPackaged
})
// Why here: the host key store is a sidecar of the same profile, and every SSH connect consults
// it. Left unbound it reports nothing trusted, which is safe but silently discards our own
@@ -267,7 +268,7 @@ export async function initializeReadyFoundation(): Promise<void> {
}
})
applyAppIcon(store.getSettings().appIcon)
if (shouldSuppressDevEducation({ isDev: is.dev })) {
if (shouldSuppressDevEducation({ isDev: !app.isPackaged })) {
suppressDevEducationForStore(store)
}
// Why: the partition installer reads the proxy through this resolver, so register it before sessions materialize.
@@ -1,10 +1,11 @@
import { mkdtempSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
import type * as DurableFileWrite from '../durable-file-write'
import { JIRA_IMAGE_SCHEME } from '../../shared/jira-attachment-image'
const ORIGINAL_PLATFORM = process.platform
const ORCA_PROFILE_ID = 'local-default'
const RETIRED_PROFILE_ID = '11111111-1111-4111-8111-111111111111'
@@ -12,6 +13,8 @@ const mocks = vi.hoisted(() => ({
// Assigned in beforeAll; the factories below read them lazily, so real directories exist by the
// time ready composition resolves the canonical userData path and the active profile directory.
userDataPath: '',
isPackaged: false,
setAppUserModelId: vi.fn(),
profileDirectory: '',
state: {
devInstanceIdentity: { appUserModelId: 'app.id', appName: 'Orca' },
@@ -50,7 +53,10 @@ vi.mock('electron', () => ({
setName: vi.fn(),
getPath: vi.fn(() => mocks.userDataPath),
getVersion: vi.fn(() => '1.0.0'),
isPackaged: false
get isPackaged() {
return mocks.isPackaged
},
setAppUserModelId: mocks.setAppUserModelId
},
session: {
defaultSession: {},
@@ -65,10 +71,6 @@ vi.mock('electron', () => ({
}))
}
}))
vi.mock('@electron-toolkit/utils', () => ({
electronApp: { setAppUserModelId: vi.fn() },
is: { dev: false }
}))
vi.mock('./main-process-state', () => ({ mainProcessState: mocks.state }))
vi.mock('../persistence', () => ({
Store: class {
@@ -145,6 +147,7 @@ vi.mock('../pty/legacy-terminal-shim-dir', () => ({ neutralizeLegacyTerminalShim
vi.mock('./windows-shell-path-hydration', () => ({
createWindowsShellPathHydration: () => ({
configure: vi.fn(),
hydrate: vi.fn(async () => {}),
whenReady: Promise.resolve()
})
}))
@@ -212,6 +215,7 @@ vi.mock('../crash-reporting/durable-crash-breadcrumb', () => ({
vi.mock('./main-window-actions', () => ({ syncMacMenuBarIcon: vi.fn() }))
vi.mock('./gpu-lifecycle', () => ({ updateGpuAccelerationAboutPanel: vi.fn() }))
vi.mock('../cli/wsl-cli-registration-reconciliation', () => ({
logWslCliReconciliationResults: vi.fn(),
reconcileManagedWslCliRegistrations: vi.fn(async () => [])
}))
vi.mock('./wsl-cli-reconciliation-startup-barrier', () => ({
@@ -313,6 +317,8 @@ describe('ready-phase browser identity authority', () => {
beforeEach(() => {
vi.resetModules()
mocks.isPackaged = false
mocks.setAppUserModelId.mockClear()
mocks.openMainWindow.mockClear()
mocks.handleProtocol.mockClear()
mocks.runtimeRpcStart.mockClear()
@@ -320,6 +326,32 @@ describe('ready-phase browser identity authority', () => {
mocks.state.isServeMode = false
})
afterEach(() => {
Object.defineProperty(process, 'platform', { value: ORIGINAL_PLATFORM })
})
it.each([
{ platform: 'win32', isPackaged: true, appId: 'app.id' },
{ platform: 'win32', isPackaged: false, appId: process.execPath },
{ platform: 'darwin', isPackaged: true, appId: null },
{ platform: 'linux', isPackaged: true, appId: null }
])(
'sets Windows app identity on $platform (packaged=$isPackaged)',
async ({ platform, isPackaged, appId }) => {
seedIdentityRecord('clean', true)
mocks.isPackaged = isPackaged
Object.defineProperty(process, 'platform', { value: platform })
await runReady()
if (appId === null) {
expect(mocks.setAppUserModelId).not.toHaveBeenCalled()
} else {
expect(mocks.setAppUserModelId).toHaveBeenCalledExactlyOnceWith(appId)
}
}
)
// The bug: ready used to mirror a retired per-profile setting into the root record, so switching
// from a native profile to a clean one started the clean profile in native. The root record read
// before ready is the only authority now, and ready must not rewrite it in either direction —
@@ -1,7 +1,6 @@
import { app, nativeTheme } from 'electron'
import { randomUUID } from 'node:crypto'
import { performance } from 'node:perf_hooks'
import { is } from '@electron-toolkit/utils'
import { StarNagService } from '../star-nag/service'
import { AgentBrowserBridge } from '../browser/agent-browser-bridge'
import { EmulatorBridge } from '../emulator/emulator-bridge'
@@ -116,7 +115,7 @@ export async function initializeReadyRuntimeServices(): Promise<void> {
return state.codexRuntimeHome.resolveHostCodexHomePathForLaunchReadOnly()
}
})
if (shouldInstallManagedHooks(is.dev)) {
if (shouldInstallManagedHooks(!app.isPackaged)) {
reconcileStartupManagedAgentHooks({
getSettings: () => store.getSettings(),
isQuitting: () => state.isQuitting,
@@ -14,7 +14,6 @@ const launchHooks = vi.hoisted(() => ({
}))
vi.mock('electron', () => ({ app: electronApp, powerMonitor: { on: vi.fn() } }))
vi.mock('@electron-toolkit/utils', () => ({ is: { dev: false } }))
vi.mock('../orca-profiles/profile-cloud-auth-config', () => ({
getOrcaCloudAuthConfig: () => ({ configured: false })
}))
@@ -1,7 +1,6 @@
import { app, type BrowserWindow } from 'electron'
import { ORCA_LAYOUT_OWNER_IS_MAIN } from '../../shared/layout-owner-switch'
import { placeUnshownLiveTerminalsBestEffort } from '../runtime/terminal/adoption/unshown-live-terminal-placement'
import { is } from '@electron-toolkit/utils'
import {
getCanonicalUserDataPath,
migrateMobilePairingDataToCanonicalUserDataPath
@@ -81,7 +80,7 @@ function installRuntimeRpc(
throw new Error(`Invalid ORCA_E2E_RUNTIME_WS_PORT value: ${requestedE2EWsPort}`)
}
// Why: pin dev to 6769 so `pnpm dev` doesn't race packaged Orca on 6768 and fall back to a random port, breaking deterministic mobile pairing/repro (STA-1511).
const devWsPort = is.dev && !isE2E ? 6769 : undefined
const devWsPort = !app.isPackaged && !isE2E ? 6769 : undefined
const runtimeRpc = new OrcaRuntimeRpcServer({
runtime,
// Why: mobile pairing needs the stable pre-setName() path (getCanonicalUserDataPath), not a late app.getPath('userData') that drops paired devices across restarts.
@@ -67,7 +67,6 @@ vi.mock('electron', () => ({
vi.mock('electron-updater', () => ({ autoUpdater: autoUpdaterMock }))
vi.mock('./electron-updater-loader', () => ({ loadElectronAutoUpdater: () => autoUpdaterMock }))
vi.mock('@electron-toolkit/utils', () => ({ is: { dev: false } }))
vi.mock('./ipc/pty/kill-all', () => ({ killAllPty: vi.fn() }))
vi.mock('./updater-changelog', () => ({ fetchChangelog: vi.fn().mockResolvedValue(null) }))
vi.mock('./updater-nudge', () => ({
@@ -7,7 +7,6 @@ const { appMock, autoUpdaterMock, chooseLocalBuildMock, moduleFactories, resetUp
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
-6
View File
@@ -45,7 +45,6 @@ type UpdaterModuleFactories = {
}
electronUpdater: () => { autoUpdater: AutoUpdaterMock }
electronUpdaterLoader: () => { loadElectronAutoUpdater: () => AutoUpdaterMock }
electronToolkitUtils: () => { is: { dev: boolean } }
ptyKillAll: () => { killAllPty: UpdaterSpy }
linuxUpdatePackageType: () => {
getLinuxPackageType: Mock<() => LinuxPackageType>
@@ -72,7 +71,6 @@ export type UpdaterMocks = {
browserWindowMock: { getAllWindows: Mock<() => unknown[]> }
nativeUpdaterMock: { on: UpdaterSpy }
autoUpdaterMock: AutoUpdaterMock
isMock: { dev: boolean }
killAllPtyMock: UpdaterSpy
powerMonitorOnMock: UpdaterSpy
getLinuxPackageTypeMock: Mock<() => LinuxPackageType>
@@ -206,7 +204,6 @@ export function createUpdaterMocks(): UpdaterMocks {
const nativeUpdaterMock = {
on: vi.fn()
}
const isMock = { dev: false }
const killAllPtyMock = vi.fn()
const powerMonitorOnMock = vi.fn()
const getLinuxRootPackageTypeMock = vi.fn<() => 'deb' | 'rpm' | null>(() => null)
@@ -237,7 +234,6 @@ export function createUpdaterMocks(): UpdaterMocks {
}),
electronUpdater: () => ({ autoUpdater: autoUpdaterMock }),
electronUpdaterLoader: () => ({ loadElectronAutoUpdater: loadGenerationScopedAutoUpdater }),
electronToolkitUtils: () => ({ is: isMock }),
ptyKillAll: () => ({ killAllPty: killAllPtyMock }),
// Why: only the marker resolver is faked so the real artifact capture/redaction path stays under test.
linuxUpdatePackageType: () => ({
@@ -282,7 +278,6 @@ export function createUpdaterMocks(): UpdaterMocks {
appMock.getVersion.mockReturnValue('1.0.51')
appMock.quit.mockReset()
appMock.isPackaged = true
isMock.dev = false
killAllPtyMock.mockReset()
armExitWatchdogMock.mockReset()
disarmExitWatchdogMock.mockReset()
@@ -319,7 +314,6 @@ export function createUpdaterMocks(): UpdaterMocks {
browserWindowMock,
nativeUpdaterMock,
autoUpdaterMock,
isMock,
killAllPtyMock,
powerMonitorOnMock,
getLinuxPackageTypeMock,
@@ -14,7 +14,6 @@ const {
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
+3 -7
View File
@@ -8,7 +8,7 @@ const { netFetchMock, netRequestMock } = vi.hoisted(() => ({
netRequestMock: vi.fn()
}))
const { appMock, browserWindowMock, nativeUpdaterMock, autoUpdaterMock, isMock, killAllPtyMock } =
const { appMock, browserWindowMock, nativeUpdaterMock, autoUpdaterMock, killAllPtyMock } =
vi.hoisted(() => {
const appEventHandlers = new Map<string, ((...args: unknown[]) => void)[]>()
const eventHandlers = new Map<string, ((...args: unknown[]) => void)[]>()
@@ -79,7 +79,7 @@ const { appMock, browserWindowMock, nativeUpdaterMock, autoUpdaterMock, isMock,
on: vi.fn()
},
autoUpdaterMock,
isMock: { dev: false },
killAllPtyMock: vi.fn()
}
})
@@ -100,10 +100,6 @@ vi.mock('./electron-updater-loader', () => ({
loadElectronAutoUpdater: () => autoUpdaterMock
}))
vi.mock('@electron-toolkit/utils', () => ({
is: isMock
}))
vi.mock('./ipc/pty/kill-all', () => ({ killAllPty: killAllPtyMock }))
vi.mock('./updater-nudge', () => ({
@@ -178,7 +174,7 @@ describe('updater check failure handling', () => {
appMock.getVersion.mockReturnValue('1.0.51')
appMock.quit.mockReset()
appMock.isPackaged = true
isMock.dev = false
killAllPtyMock.mockReset()
vi.unstubAllGlobals()
vi.useRealTimers()
-1
View File
@@ -14,7 +14,6 @@ const {
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
@@ -13,7 +13,6 @@ const {
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
@@ -7,7 +7,6 @@ const { autoUpdaterMock, fetchNewerReleaseTagsMock, moduleFactories, resetUpdate
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
@@ -95,7 +95,6 @@ vi.mock('./linux-update-package-type', () => ({
getLinuxRootPackageType: () => null,
isExternallyManagedLinuxInstall: () => false
}))
vi.mock('@electron-toolkit/utils', () => ({ is: { dev: false } }))
vi.mock('./ipc/pty/kill-all', () => ({ killAllPty: killAllPtyMock }))
vi.mock('./updater-changelog', () => ({ fetchChangelog: vi.fn().mockResolvedValue(null) }))
vi.mock('./updater-nudge', () => ({
@@ -9,7 +9,6 @@ const {
browserWindowMock,
nativeUpdaterMock,
autoUpdaterMock,
isMock,
killAllPtyMock,
recordUpdaterLifecycleMock
} = vi.hoisted(() => {
@@ -79,7 +78,7 @@ const {
browserWindowMock: { getAllWindows: vi.fn(() => []) },
nativeUpdaterMock: { on: vi.fn() },
autoUpdaterMock,
isMock: { dev: false },
killAllPtyMock: vi.fn(),
recordUpdaterLifecycleMock: vi.fn()
}
@@ -98,7 +97,6 @@ vi.mock('electron-updater', () => ({ autoUpdater: autoUpdaterMock }))
vi.mock('./electron-updater-loader', () => ({
loadElectronAutoUpdater: () => autoUpdaterMock
}))
vi.mock('@electron-toolkit/utils', () => ({ is: isMock }))
vi.mock('./ipc/pty/kill-all', () => ({ killAllPty: killAllPtyMock }))
vi.mock('./updater-changelog', () => ({
fetchChangelog: vi.fn().mockResolvedValue(null)
@@ -196,7 +194,7 @@ describe('quitAndInstall failure carries the updater cause', () => {
appMock.getVersion.mockReturnValue('1.4.162')
appMock.quit.mockReset()
appMock.isPackaged = true
isMock.dev = false
killAllPtyMock.mockReset()
recordUpdaterLifecycleMock.mockReset()
Object.defineProperty(process, 'platform', {
@@ -16,7 +16,6 @@ const {
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
@@ -21,7 +21,6 @@ const {
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
+2 -7
View File
@@ -8,7 +8,6 @@ const {
nativeUpdaterMock,
autoUpdaterMock,
shellMock,
isMock,
killAllPtyMock,
getMacUpdateRunningInstancesMock,
prepareShipItStagingMock,
@@ -87,7 +86,7 @@ const {
shellMock: {
openExternal: vi.fn()
},
isMock: { dev: false },
getMacUpdateRunningInstancesMock: vi.fn(async (): Promise<number[]> => []),
recoverBrowserSidecarsMock: vi.fn(async (_executable: string, pids: number[]) => pids),
describeBrowserBlockersMock: vi.fn(async (_executable: string, pids: number[]) =>
@@ -115,10 +114,6 @@ vi.mock('./electron-updater-loader', () => ({
loadElectronAutoUpdater: () => autoUpdaterMock
}))
vi.mock('@electron-toolkit/utils', () => ({
is: isMock
}))
vi.mock('./ipc/pty/kill-all', () => ({ killAllPty: killAllPtyMock }))
vi.mock('./updater-changelog', () => ({
@@ -182,7 +177,7 @@ describe('updater mac install handoff', () => {
appMock.getVersion.mockReturnValue('1.0.51')
appMock.quit.mockReset()
appMock.isPackaged = true
isMock.dev = false
killAllPtyMock.mockReset()
getMacUpdateRunningInstancesMock.mockReset().mockResolvedValue([])
recoverBrowserSidecarsMock
-1
View File
@@ -14,7 +14,6 @@ const {
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
@@ -7,7 +7,6 @@ const { appMock, autoUpdaterMock, fetchNewerReleaseTagsMock, moduleFactories, re
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
@@ -14,7 +14,6 @@ const {
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
@@ -20,7 +20,6 @@ const { launchPathScope } = vi.hoisted(() => ({
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
+1 -3
View File
@@ -4,7 +4,6 @@ import { loadUpdaterModule, warmUpdaterModule } from './updater-test-module-load
const {
appMock,
autoUpdaterMock,
isMock,
powerMonitorOnMock,
fetchNudgeMock,
shouldApplyNudgeMock,
@@ -15,7 +14,6 @@ const {
vi.mock('electron', () => moduleFactories.electron())
vi.mock('electron-updater', () => moduleFactories.electronUpdater())
vi.mock('./electron-updater-loader', () => moduleFactories.electronUpdaterLoader())
vi.mock('@electron-toolkit/utils', () => moduleFactories.electronToolkitUtils())
vi.mock('./ipc/pty/kill-all', () => moduleFactories.ptyKillAll())
vi.mock('./linux-update-package-type', () => moduleFactories.linuxUpdatePackageType())
vi.mock('./updater-lifecycle-diagnostics', () => moduleFactories.updaterLifecycleDiagnostics())
@@ -35,7 +33,7 @@ describe('updater', () => {
})
it('does not load or configure electron-updater during dev setup', async () => {
isMock.dev = true
appMock.isPackaged = false
const mainWindow = { webContents: { send: vi.fn() } }
const { setupAutoUpdater } = await loadUpdaterModule()
+1 -2
View File
@@ -1,5 +1,4 @@
import { app } from 'electron'
import { is } from '@electron-toolkit/utils'
import {
DEV_CHANNEL_PLATFORM_LABEL,
getVersionChannel,
@@ -90,7 +89,7 @@ export abstract class UpdaterBuildSelection extends UpdaterMenuChecks {
/** Pins the updater at one exact release tag and checks it, so a dev can move to any published build on any channel — including an older one. */
protected async checkForPinnedBuild(channel: ReleaseChannel, tag: string): Promise<void> {
if (!app.isPackaged || is.dev) {
if (!app.isPackaged) {
this.sendStatus({ state: 'not-available', userInitiated: true })
return
}
+1 -2
View File
@@ -1,5 +1,4 @@
import { app } from 'electron'
import { is } from '@electron-toolkit/utils'
import { isMacInstallRequested } from '../updater-mac-install'
import type { UpdateCheckOptions } from '../../shared/update-status-types'
import type { ReleaseChannel } from '../../shared/release-channel'
@@ -15,7 +14,7 @@ export abstract class UpdaterMenuChecks extends UpdaterScheduling {
) {
return
}
if (!app.isPackaged || is.dev) {
if (!app.isPackaged) {
this.sendStatus({ state: 'not-available', userInitiated: true })
return
}
+1 -2
View File
@@ -1,5 +1,4 @@
import { app } from 'electron'
import { is } from '@electron-toolkit/utils'
import { fetchNudge, shouldApplyNudge } from '../updater-nudge'
import { NUDGE_ACTIVATION_COOLDOWN_MS, NUDGE_POLL_INTERVAL_MS } from './updater-state'
import { UpdaterBuildSelection } from './updater-build-selection'
@@ -7,7 +6,7 @@ import { UpdaterBuildSelection } from './updater-build-selection'
/** Polls update campaigns and exposes their dismissal actions. */
export abstract class UpdaterNudge extends UpdaterBuildSelection {
protected async checkForUpdateNudge(): Promise<void> {
if (!app.isPackaged || is.dev) {
if (!app.isPackaged) {
return
}
if (this.nudgeCheckInFlight) {
+1 -2
View File
@@ -1,5 +1,4 @@
import { app } from 'electron'
import { is } from '@electron-toolkit/utils'
import type { UpdateCheckOptions, UpdateStatus } from '../../shared/update-status-types'
import type {
RemoteServerUpdateInstallResult,
@@ -18,7 +17,7 @@ export abstract class UpdaterRemoteStatus extends UpdaterNudge {
}
protected getRemoteServerUpdateSupport(): RemoteServerUpdateSupport {
if (!app.isPackaged || is.dev) {
if (!app.isPackaged) {
return {
installMode: this.updateInstallMode,
automatic: false,
+1 -2
View File
@@ -1,5 +1,4 @@
import { app } from 'electron'
import { is } from '@electron-toolkit/utils'
import { isMacInstallRequested } from '../updater-mac-install'
import { withUpdaterSpan } from '../observability/instrumentation'
import {
@@ -65,7 +64,7 @@ export abstract class UpdaterScheduling extends UpdaterCheckFailure {
if (this.backgroundCheckLaunchPending || this.currentStatus.state === 'checking') {
return false
}
if (!app.isPackaged || is.dev) {
if (!app.isPackaged) {
this.sendStatus({ state: 'not-available' })
return false
}
+1 -5
View File
@@ -1,6 +1,5 @@
import { app, powerMonitor } from 'electron'
import type { BrowserWindow } from 'electron'
import { is } from '@electron-toolkit/utils'
import type { ReleaseBuild, ReleaseChannel } from '../../shared/release-channel'
import type { ReleaseBuildListOptions } from '../updater-release-build-cache'
import type {
@@ -137,10 +136,7 @@ export class UpdaterSetup extends UpdaterDownloadInstall {
this.sendErrorStatus(`The server update did not complete: ${serveHandoffFailure}`, true)
}
if (!app.isPackaged && !is.dev) {
return
}
if (is.dev) {
if (!app.isPackaged) {
return
}
@@ -3,9 +3,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -3,9 +3,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -3,9 +3,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -12,9 +12,6 @@ vi.mock('../crash-reporting/durable-crash-breadcrumb', async (importOriginal) =>
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -33,7 +30,7 @@ vi.mock('../browser/browser-client-page-renderer-runtime', async () => {
import { createMainWindow } from './createMainWindow'
import {
browserWindowMock,
isMock,
appMock,
powerMonitorOnMock,
resetMainWindowMocks
} from './createMainWindow-test-harness'
@@ -404,7 +401,7 @@ describe('renderer recovery reload watchdog', () => {
it('gives the dev server a longer budget than a packaged load', () => {
const onRecoveryReloadOutcome = vi.fn()
const { browserWindowInstance, consoleError, crashRenderer } = createHarness()
isMock.dev = true
appMock.isPackaged = false
vi.stubEnv('ELECTRON_RENDERER_URL', 'http://localhost:5173/')
try {
@@ -3,9 +3,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -3,9 +3,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -3,9 +3,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -3,9 +3,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -3,9 +3,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -17,7 +14,7 @@ vi.mock('../browser/browser-manager', async () =>
import { createMainWindow } from './createMainWindow'
import { ipcMain } from 'electron'
import { resetExpectedTeardownStateForTest } from '../crash-reporting/expected-teardown-state'
import { browserWindowMock, isMock, resetMainWindowMocks } from './createMainWindow-test-harness'
import { browserWindowMock, appMock, resetMainWindowMocks } from './createMainWindow-test-harness'
describe('createMainWindow', () => {
beforeEach(() => {
@@ -751,7 +748,7 @@ describe('createMainWindow', () => {
})
it('toggles devtools on F12 in development', () => {
isMock.dev = true
appMock.isPackaged = false
const windowHandlers: Record<string, (...args: any[]) => void> = {}
const webContents = {
@@ -27,7 +27,11 @@ export const notificationMock: Mock<(...args: unknown[]) => { show: MainWindowSp
export const powerMonitorOnMock: MainWindowSpy = vi.fn()
export const powerMonitorRemoveListenerMock: MainWindowSpy = vi.fn()
export const routePartitionAllowedMock: Mock<(partition: string) => boolean> = vi.fn(() => false)
export const isMock = { dev: false }
export const appMock: { isPackaged: boolean; on: MainWindowSpy; removeListener: MainWindowSpy } = {
isPackaged: true,
on: vi.fn(),
removeListener: vi.fn()
}
export const macosTahoeMock = { value: false }
type IpcMainMock = {
@@ -45,7 +49,7 @@ const ipcMainMock: IpcMainMock = {
}
export type ElectronModuleMock = {
app: { on: MainWindowSpy; removeListener: MainWindowSpy }
app: typeof appMock
BrowserWindow: typeof browserWindowMock
ipcMain: IpcMainMock
Menu: { buildFromTemplate: typeof buildFromTemplateMock }
@@ -70,7 +74,7 @@ export type BrowserManagerModuleMock = {
// re-declaring the shared electron surface.
export function electronModuleMock(): ElectronModuleMock {
return {
app: { on: vi.fn(), removeListener: vi.fn() },
app: appMock,
BrowserWindow: browserWindowMock,
ipcMain: ipcMainMock,
Menu: { buildFromTemplate: buildFromTemplateMock },
@@ -85,12 +89,6 @@ export function electronModuleMock(): ElectronModuleMock {
}
}
export function electronToolkitUtilsMock() {
return {
is: isMock
}
}
export function macosTahoeReleaseMock() {
return {
isMacosTahoeOrNewer: vi.fn(() => macosTahoeMock.value)
@@ -129,7 +127,7 @@ export function resetMainWindowMocks(): void {
powerMonitorRemoveListenerMock.mockReset()
routePartitionAllowedMock.mockReset()
routePartitionAllowedMock.mockReturnValue(false)
isMock.dev = false
appMock.isPackaged = true
macosTahoeMock.value = false
ipcMainMock.on.mockReset()
ipcMainMock.removeListener.mockReset()
@@ -7,9 +7,6 @@ const { recordAgentSessionRuntimeEndMock } = vi.hoisted(() => ({
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -3,9 +3,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
@@ -3,9 +3,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
-3
View File
@@ -3,9 +3,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('electron', async () =>
(await import('./createMainWindow-test-harness')).electronModuleMock()
)
vi.mock('@electron-toolkit/utils', async () =>
(await import('./createMainWindow-test-harness')).electronToolkitUtilsMock()
)
vi.mock('./macos-tahoe-release', async () =>
(await import('./createMainWindow-test-harness')).macosTahoeReleaseMock()
)
+2 -3
View File
@@ -1,5 +1,4 @@
import { BrowserWindow, nativeTheme, powerMonitor, screen } from 'electron'
import { is } from '@electron-toolkit/utils'
import { app, BrowserWindow, nativeTheme, powerMonitor, screen } from 'electron'
import { join } from 'node:path'
import { getAppIconPath } from '../app-icon'
import { browserManager } from '../browser/browser-manager'
@@ -39,7 +38,7 @@ export { WINDOW_QUIT_RENDERER_ACK_TIMEOUT_MS }
export function loadMainWindow(mainWindow: BrowserWindow, observer?: MainWindowLoadObserver): void {
const load =
is.dev && process.env.ELECTRON_RENDERER_URL
!app.isPackaged && process.env.ELECTRON_RENDERER_URL
? mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL)
: mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
// Observe each load promise so failures cannot leave recovery waiting silently.
@@ -11,7 +11,7 @@ const {
getAllDisplaysMock,
installNavigationPolicyMock,
sendToTrustedUIRendererMock,
isMock
appMock
} = vi.hoisted(() => {
const created: FakeWindow[] = []
@@ -112,7 +112,7 @@ const {
]),
installNavigationPolicyMock: vi.fn(),
sendToTrustedUIRendererMock: vi.fn(),
isMock: { dev: false } as { dev: boolean }
appMock: { isPackaged: true }
}
})
@@ -122,13 +122,18 @@ const {
instances
vi.mock('electron', () => ({
app: { on: appOnMock, removeListener: appRemoveListenerMock },
app: {
on: appOnMock,
removeListener: appRemoveListenerMock,
get isPackaged() {
return appMock.isPackaged
}
},
BrowserWindow: BrowserWindowMock,
nativeTheme: nativeThemeMock,
screen: { getAllDisplays: getAllDisplaysMock }
}))
vi.mock('@electron-toolkit/utils', () => ({ is: isMock }))
vi.mock('../ipc/ui', () => ({ sendToTrustedUIRenderer: sendToTrustedUIRendererMock }))
vi.mock('./privileged-window-navigation', () => ({
installPrivilegedWindowNavigationPolicy: installNavigationPolicyMock
@@ -182,7 +187,7 @@ afterEach(() => vi.unstubAllEnvs())
describe('createOrFocusDashboardPopout', () => {
beforeEach(() => {
instances.length = 0
isMock.dev = false
appMock.isPackaged = true
vi.stubEnv('ELECTRON_RENDERER_URL', '')
getAllDisplaysMock.mockReturnValue([{ workArea: { x: 0, y: 0, width: 1920, height: 1080 } }])
})
@@ -246,7 +251,7 @@ describe('createOrFocusDashboardPopout', () => {
})
it('loads the dev server URL when in dev', () => {
isMock.dev = true
appMock.isPackaged = false
vi.stubEnv('ELECTRON_RENDERER_URL', RENDERER_URL)
createOrFocusDashboardPopout(makeStore() as never)
const win = instances[0]
+1 -2
View File
@@ -1,6 +1,5 @@
import { app, BrowserWindow, nativeTheme, type WebContents, type Event } from 'electron'
import { join } from 'node:path'
import { is } from '@electron-toolkit/utils'
import type { Store } from '../persistence'
import { isBackgroundLaunch, showWindowWithoutStealingFocus } from './foreground-activation-policy'
import { rectHasVisibleAreaOnAnyDisplay } from './window-bounds-validation'
@@ -101,7 +100,7 @@ function broadcastPopoutOpenChanged(open: boolean): void {
function loadDashboardPopout(window: BrowserWindow): void {
// Why: mirror loadMainWindow's dev/prod branch — the dev server serves the
// second HTML entry, prod loads the emitted file.
if (is.dev && process.env.ELECTRON_RENDERER_URL) {
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
void window.loadURL(`${process.env.ELECTRON_RENDERER_URL}/popout.html`)
} else {
void window.loadFile(join(__dirname, '../renderer/popout.html'))
@@ -1,5 +1,5 @@
import { app } from 'electron'
import type { BrowserWindow } from 'electron'
import { is } from '@electron-toolkit/utils'
import {
ModifierDoubleTapDetector,
toModifierDoubleTapEvent
@@ -116,7 +116,7 @@ export function installMainWindowShortcutRouting(args: {
return
}
if (input.type === 'keyDown' && is.dev && input.code === 'F12') {
if (input.type === 'keyDown' && !app.isPackaged && input.code === 'F12') {
event.preventDefault()
if (mainWindow.webContents.isDevToolsOpened()) {
mainWindow.webContents.closeDevTools()
@@ -7,7 +7,7 @@ import {
RENDERER_RECOVERY_LOAD_TIMEOUT_MS
} from './renderer-recovery-reload-watchdog'
vi.mock('@electron-toolkit/utils', () => ({ is: { dev: false } }))
vi.mock('electron', () => ({ app: { isPackaged: true } }))
function createHarness() {
const webContents = Object.assign(new EventEmitter(), { id: 143 })
@@ -1,4 +1,4 @@
import { is } from '@electron-toolkit/utils'
import { app } from 'electron'
import type { BrowserWindow } from 'electron'
import { isSystemSessionEnding } from '../crash-reporting/expected-teardown-state'
import type { LowCommitOomVerdict } from '../crash-reporting/low-commit-oom-recovery-gate'
@@ -108,7 +108,7 @@ export function createRendererRecoveryReloadWatchdog(args: {
}
// Match loadMainWindow's dev/prod branch.
const timeoutMs = (): number =>
is.dev && process.env.ELECTRON_RENDERER_URL
!app.isPackaged && process.env.ELECTRON_RENDERER_URL
? RENDERER_RECOVERY_DEV_LOAD_TIMEOUT_MS
: RENDERER_RECOVERY_LOAD_TIMEOUT_MS
+2 -3
View File
@@ -1,6 +1,5 @@
import { BrowserWindow, nativeTheme, type WebContents } from 'electron'
import { app, BrowserWindow, nativeTheme, type WebContents } from 'electron'
import { join } from 'node:path'
import { is } from '@electron-toolkit/utils'
import { isBackgroundLaunch, showWindowWithoutStealingFocus } from './foreground-activation-policy'
import { installPrivilegedWindowNavigationPolicy } from './privileged-window-navigation'
import { sendToTrustedUIRenderer } from '../ipc/ui'
@@ -85,7 +84,7 @@ export function createOrFocusWorkspaceSpaceWindow(store: Store): BrowserWindow {
}
})
if (is.dev && process.env.ELECTRON_RENDERER_URL) {
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
void window.loadURL(`${process.env.ELECTRON_RENDERER_URL}/space.html`)
} else {
void window.loadFile(join(__dirname, '../renderer/space.html'))