Files
orca/src/shared/constants.test.ts
T
Jinjing 1536171fdb Add a native macOS menu bar status item with activity indicator (#9042)
* Add native macOS menu bar status item with settings toggle

Extend the Windows-only system tray into a shared status-item module
so macOS gets a template menu bar icon (Open/Settings/Check for
Updates/Quit), a theme-aware attention dot, and a "Show Menu Bar
Icon" appearance setting. Also fixes a startup race where a tray
"Settings…" click could fire before the renderer's ui:openSettings
listener attached, by queuing a one-shot pending-open-settings intent
the renderer consumes on mount.

* Fix Retina blur, race conditions, and menu-label duplication in tray Set

- Rebuild the tray attention icon's @2x representation since toBitmap only
  read 1x pixels, blurring the glyph on Retina displays
- Fix premultiplied-alpha math so light-glyph tinting uses per-pixel alpha
  instead of a flat 0xff, keeping antialiased edges valid
- Always push ui:openSettings and leave a longer-lived pending flag, since
  there was no reliable signal that a renderer's listener was attached
- Preserve tray attention state across macOS menu-bar hide/show toggles
  instead of resetting it on tray destroy
- Route macOS tray creation through syncMacMenuBarIcon so startup and the
  live toggle share one visibility policy
- Reuse app-menu translation keys for tray Settings/Check for Updates and
  drop the now-duplicate tray-scoped locale strings

* Make menu bar icon default on and platform-independent in settings

Previously showMenuBarIcon was hardcoded to darwin-only in both the
default settings and the sanitize/load paths, so a profile written on
macOS lost its opt-out when touched from another OS. Store the raw
boolean everywhere and let darwin-specific consumers decide whether
to act on it, so the value round-trips unchanged across platforms.

* Fix Settings menu click being silently dropped after a slow cold rendere

Replace the 60s pendingOpenSettings TTL with an untimed intent, since a
cold renderer start can outrun any fixed timeout and cause the flag to
expire before the click is consumed. webContents-id scoping plus
consume-on-read still prevent the intent from leaking to an unrelated
renderer. Adds a test covering the queued-before-mount pull path.
2026-07-16 13:06:21 -07:00

157 lines
5.7 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
getDefaultNotificationSettings,
getDefaultPrimarySelectionMiddleClickPaste,
getDefaultTerminalRightClickToPaste,
getDefaultSettings
} from './constants'
describe('getDefaultSettings', () => {
it('uses platform-consistent separators for the default workspace directory', () => {
expect(getDefaultSettings('/Users/alice').workspaceDir).toBe('/Users/alice/orca/workspaces')
expect(getDefaultSettings('C:\\Users\\alice').workspaceDir).toBe(
'C:\\Users\\alice\\orca\\workspaces'
)
})
it('enables gitignored file decorations by default', () => {
expect(getDefaultSettings('/tmp').showGitIgnoredFiles).toBe(true)
})
it('uses list view for Source Control changes by default', () => {
expect(getDefaultSettings('/tmp').sourceControlViewMode).toBe('list')
})
it('keeps Source Control changes first by default', () => {
expect(getDefaultSettings('/tmp').sourceControlGroupOrder).toBe('changes-first')
})
it('keeps first-work branch auto-renaming on by default for new settings', () => {
expect(getDefaultSettings('/tmp').autoRenameBranchFromWork).toBe(true)
expect(getDefaultSettings('/tmp').autoRenameBranchFromWorkDefaultedOn).toBe(true)
})
it('uses a block terminal cursor by default for new settings', () => {
expect(getDefaultSettings('/tmp').terminalCursorStyle).toBe('block')
expect(getDefaultSettings('/tmp').terminalCursorStyleDefaultedToBlock).toBe(true)
})
it('enables separate light terminal theme by default', () => {
expect(getDefaultSettings('/tmp').terminalUseSeparateLightTheme).toBe(true)
})
it('asks before closing terminals with running processes by default', () => {
expect(getDefaultSettings('/tmp').skipCloseTerminalWithRunningProcessConfirm).toBe(false)
})
it('uses system language by default', () => {
expect(getDefaultSettings('/tmp').uiLanguage).toBe('system')
})
it('defaults the menu bar icon on so the value round-trips across platforms', () => {
expect(getDefaultSettings('/tmp').showMenuBarIcon).toBe(true)
})
it('confirms before closing pinned tabs by default', () => {
expect(getDefaultSettings('/tmp').confirmClosePinnedTab).toBe(true)
})
it('keeps file-editor word wrapping enabled by default', () => {
expect(getDefaultSettings('/tmp').editorWordWrap).toBe(true)
})
it('keeps rich Markdown spellcheck enabled by default', () => {
expect(getDefaultSettings('/tmp').richMarkdownSpellcheckEnabled).toBe(true)
})
it('enables Source Control AI by default without pinning a separate agent', () => {
expect(getDefaultSettings('/tmp').commitMessageAi).toMatchObject({
enabled: true,
agentId: null,
selectedModelByAgent: {}
})
expect(getDefaultSettings('/tmp').sourceControlAi).toMatchObject({
enabled: true,
agentId: null,
selectedModelByAgent: {},
instructionsByOperation: {
commitMessage: '',
pullRequest: '',
branchName: ''
}
})
})
it('keeps compact worktree cards disabled by default', () => {
expect(getDefaultSettings('/tmp').compactWorktreeCards).toBe(false)
})
it('keeps per-workspace environments disabled by default', () => {
expect(getDefaultSettings('/tmp').experimentalEphemeralVms).toBe(false)
})
it('defaults local Windows projects to the host runtime', () => {
expect(getDefaultSettings('/tmp').localWindowsRuntimeDefault).toEqual({
kind: 'windows-host'
})
})
it('suppresses notifications for the focused worktree by default for new users', () => {
expect(getDefaultNotificationSettings().suppressWhenFocused).toBe(true)
expect(getDefaultSettings('/tmp').notifications.suppressWhenFocused).toBe(true)
})
it('defaults agent launch args to yolo mode where the CLI supports it', () => {
const settings = getDefaultSettings('/tmp')
expect(settings.agentDefaultArgs).toMatchObject({
claude: '--dangerously-skip-permissions',
codex: '--dangerously-bypass-approvals-and-sandbox',
gemini: '--yolo',
cursor: '--yolo',
copilot: '--yolo',
grok: '--permission-mode bypassPermissions'
})
expect(settings.agentDefaultArgs).not.toHaveProperty('opencode')
expect(settings.agentDefaultArgs).not.toHaveProperty('kilo')
expect(settings.agentDefaultEnv).toMatchObject({
goose: { GOOSE_MODE: 'auto' }
})
expect(settings.agentYoloDefaultsMigrated).toBe(true)
})
})
describe('getDefaultPrimarySelectionMiddleClickPaste', () => {
it('enables primary selection paste on Linux by default', () => {
expect(getDefaultPrimarySelectionMiddleClickPaste('linux')).toBe(true)
})
it('enables primary selection paste on macOS by default', () => {
expect(getDefaultPrimarySelectionMiddleClickPaste('darwin')).toBe(true)
})
it('leaves primary selection paste opt-in on Windows', () => {
expect(getDefaultPrimarySelectionMiddleClickPaste('win32')).toBe(false)
})
})
describe('getDefaultTerminalRightClickToPaste', () => {
it('defaults on only for Windows', () => {
expect(getDefaultTerminalRightClickToPaste('win32')).toBe(true)
expect(getDefaultTerminalRightClickToPaste('darwin')).toBe(false)
expect(getDefaultTerminalRightClickToPaste('linux')).toBe(false)
})
})
describe('MiniMax defaults', () => {
it('starts MiniMax with empty group id and the canonical default model', () => {
const settings = getDefaultSettings('/tmp')
// Why: the fetcher reads these defaults on first launch. An empty
// group id is the signal that the fetcher must pull the value from
// the cookie itself, and "general" matches the model name the
// MiniMax usage endpoint exposes by default.
expect(settings.minimaxGroupId).toBe('')
expect(settings.minimaxUsageModels).toBe('general')
})
})