test: remove low-value account and surface settings suites (#38323)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-11-04 19:13:00 -08:00
committed by GitHub
co-authored by OrcaWin
parent fd8a77356b
commit 74e997ef94
7 changed files with 16 additions and 1342 deletions
+13 -36
View File
@@ -1403,7 +1403,7 @@
"id": "settings.accounts-section-lifetime",
"title": "Accounts search preserves matching account section owners",
"maturity": "experimental",
"protection": "partial",
"protection": "none",
"owner": "renderer-settings",
"layer": "renderer-unit",
"surfaces": [
@@ -1413,53 +1413,29 @@
],
"platforms": ["macos", "linux", "windows"],
"providers": ["local", "remote-runtime", "ssh", "wsl"],
"coveredPlatforms": ["macos"],
"coveredPlatforms": [],
"coveredProviders": [],
"coverageNotes": "Actual AccountsPane, Grok, Cursor and Codex login components; synthetic APIs/store with Windows account support enabled/disabled. No real account, keychain or provider action.",
"coverageNotes": "Audit1437 removes AccountsPane.section-lifetime.test.tsx: DOM identity, mock status/read/subscription counts and simulated platform variants are intentionally retired. No equivalent replacement coverage is claimed. Historical coverage account: Actual AccountsPane, Grok, Cursor and Codex login components; synthetic APIs/store with Windows account support enabled/disabled. No real account, keychain or provider action.",
"motivatingLinks": [
"https://github.com/stablyai/orca/blob/main/src/renderer/src/components/settings/accounts/AccountsPane.tsx"
],
"invariant": "Matching sections retain their owners as earlier search results disappear; genuine removal releases the Codex subscription and reopens fresh status reads.",
"oracle": "Across nine matching Grok, ten matching Cursor and eleven matching Codex queries each matched section keeps the same DOM node and its status-read, watcher and subscription counts do not grow, against four to six reads each before. Explicit refresh, store-published usage updates, pushed login URLs and genuine removal all still reach the retained section.",
"commands": [
"ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/settings/accounts/AccountsPane.section-lifetime.test.tsx"
],
"testFiles": [
"src/renderer/src/components/settings/accounts/AccountsPane.section-lifetime.test.tsx"
],
"assertionRefs": [
{
"file": "src/renderer/src/components/settings/accounts/AccountsPane.section-lifetime.test.tsx",
"assertions": [
"preserves explicit Grok refresh and real hide/reopen status reads",
"keeps the login subscription until Codex genuinely hides",
"keeps Cursor refresh driven by usage updates and genuine reopen"
]
}
],
"evidenceRuns": [
{
"date": "2026-09-26",
"runner": "local",
"platform": "macos",
"command": "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/settings/AccountsPane.section-lifetime.test.tsx",
"currentCommand": "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/settings/accounts/AccountsPane.section-lifetime.test.tsx",
"result": "passed",
"durationSeconds": 1.84,
"summary": "10 tests/1 suite pass after the shared SettingsSectionStack adoption. Read-count oracles restated as node identity plus non-growing counts; original main fails the three retained-section cases and the retained Codex subscription, and passes the explicit-refresh, usage-driven and genuine-removal controls."
}
],
"oracle": "No current automated oracle is claimed after Audit1437 removed this suite. Historical oracle account: Across nine matching Grok, ten matching Cursor and eleven matching Codex queries each matched section keeps the same DOM node and its status-read, watcher and subscription counts do not grow, against four to six reads each before. Explicit refresh, store-published usage updates, pushed login URLs and genuine removal all still reach the retained section.",
"commands": [],
"testFiles": [],
"assertionRefs": [],
"evidenceRuns": [],
"runtimeBudget": {
"p95Seconds": 10,
"scope": "Focused renderer-unit suites, p95 not established."
},
"flakeHistory": {
"status": "not-started",
"evidence": "Author and independent local evidence only; no CI soak."
"evidence": "No current suite remains. Historical account: Author and independent local evidence only; no CI soak."
},
"redGreenEvidence": {
"status": "complete",
"evidence": "Candidate ten pass; current-main original seven fail and three controls pass."
"status": "missing",
"evidence": "Historical red/green only after suite removal. Candidate ten pass; current-main original seven fail and three controls pass."
},
"performanceBudget": {
"required": true,
@@ -1470,7 +1446,8 @@
"No native Linux/Windows or live SSH/WSL/paired provider behavior; platform flags are simulated.",
"Hidden Electron Grok proof uses the original audit baseline and synthetic status; current-main Cursor addition has unit evidence only.",
"Matching search no longer incidentally refreshes Grok/Cursor status or pending Codex URLs. Every one of those surfaces is store-subscribed or push-subscribed, so the retained section still tracks usage updates, pushed login URLs, explicit refresh and true hide/reopen; there is no bounded automatic freshness deadline and none of them relied on one before.",
"Child drafts, errors, copied feedback and timers remain until their own transition or real removal."
"Child drafts, errors, copied feedback and timers remain until their own transition or real removal.",
"Audit1437 removes AccountsPane.section-lifetime.test.tsx: DOM identity, mock status/read/subscription counts and simulated platform variants are intentionally retired. No equivalent replacement coverage is claimed. Original complete gate record: {\"id\":\"settings.accounts-section-lifetime\",\"title\":\"Accounts search preserves matching account section owners\",\"maturity\":\"experimental\",\"protection\":\"partial\",\"owner\":\"renderer-settings\",\"layer\":\"renderer-unit\",\"surfaces\":[\"Accounts settings search\",\"Grok and Cursor account status\",\"Codex pending sign-in link\"],\"platforms\":[\"macos\",\"linux\",\"windows\"],\"providers\":[\"local\",\"remote-runtime\",\"ssh\",\"wsl\"],\"coveredPlatforms\":[\"macos\"],\"coveredProviders\":[],\"coverageNotes\":\"Actual AccountsPane, Grok, Cursor and Codex login components; synthetic APIs/store with Windows account support enabled/disabled. No real account, keychain or provider action.\",\"motivatingLinks\":[\"https://github.com/stablyai/orca/blob/main/src/renderer/src/components/settings/accounts/AccountsPane.tsx\"],\"invariant\":\"Matching sections retain their owners as earlier search results disappear; genuine removal releases the Codex subscription and reopens fresh status reads.\",\"oracle\":\"Across nine matching Grok, ten matching Cursor and eleven matching Codex queries each matched section keeps the same DOM node and its status-read, watcher and subscription counts do not grow, against four to six reads each before. Explicit refresh, store-published usage updates, pushed login URLs and genuine removal all still reach the retained section.\",\"commands\":[\"ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/settings/accounts/AccountsPane.section-lifetime.test.tsx\"],\"testFiles\":[\"src/renderer/src/components/settings/accounts/AccountsPane.section-lifetime.test.tsx\"],\"assertionRefs\":[{\"file\":\"src/renderer/src/components/settings/accounts/AccountsPane.section-lifetime.test.tsx\",\"assertions\":[\"preserves explicit Grok refresh and real hide/reopen status reads\",\"keeps the login subscription until Codex genuinely hides\",\"keeps Cursor refresh driven by usage updates and genuine reopen\"]}],\"evidenceRuns\":[{\"date\":\"2026-09-26\",\"runner\":\"local\",\"platform\":\"macos\",\"command\":\"ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/settings/AccountsPane.section-lifetime.test.tsx\",\"currentCommand\":\"ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/settings/accounts/AccountsPane.section-lifetime.test.tsx\",\"result\":\"passed\",\"durationSeconds\":1.84,\"summary\":\"10 tests/1 suite pass after the shared SettingsSectionStack adoption. Read-count oracles restated as node identity plus non-growing counts; original main fails the three retained-section cases and the retained Codex subscription, and passes the explicit-refresh, usage-driven and genuine-removal controls.\"}],\"runtimeBudget\":{\"p95Seconds\":10,\"scope\":\"Focused renderer-unit suites, p95 not established.\"},\"flakeHistory\":{\"status\":\"not-started\",\"evidence\":\"Author and independent local evidence only; no CI soak.\"},\"redGreenEvidence\":{\"status\":\"complete\",\"evidence\":\"Candidate ten pass; current-main original seven fail and three controls pass.\"},\"performanceBudget\":{\"required\":true,\"evidence\":\"Actual renderer status-read/subscription admissions counted; the section map now lives in the shared SettingsSectionStack and no cache, poll, listener or process is added. No CPU, latency or heap claim. Matching sections retain local state and lose only the incidental search-driven refresh that duplicated an existing subscription.\"},\"knownGaps\":[\"CI soak pending.\",\"No native Linux/Windows or live SSH/WSL/paired provider behavior; platform flags are simulated.\",\"Hidden Electron Grok proof uses the original audit baseline and synthetic status; current-main Cursor addition has unit evidence only.\",\"Matching search no longer incidentally refreshes Grok/Cursor status or pending Codex URLs. Every one of those surfaces is store-subscribed or push-subscribed, so the retained section still tracks usage updates, pushed login URLs, explicit refresh and true hide/reopen; there is no bounded automatic freshness deadline and none of them relied on one before.\",\"Child drafts, errors, copied feedback and timers remain until their own transition or real removal.\"],\"promotionCriteria\":[\"Retain read/subscription counts and genuine-removal controls; collect CI soak and relevant full-app provider evidence.\"],\"demotionRule\":\"Keep experimental until soak; investigate owner and refresh regressions without weakening assertions or adding retries.\"}"
],
"promotionCriteria": [
"Retain read/subscription counts and genuine-removal controls; collect CI soak and relevant full-app provider evidence."
-3
View File
@@ -7839,9 +7839,6 @@
"src/renderer/src/components/right-sidebar/source-control/ai/useSourceControlAgentActionDialog.test.tsx": 6336,
"src/renderer/src/components/right-sidebar/source-control/listing/useSourceControlSubmoduleStatus.test.tsx": 2076,
"src/renderer/src/components/settings/accounts/AccountsPane.owner-scope.test.tsx": 4057,
"src/renderer/src/components/settings/accounts/AccountsPane.section-lifetime.test.tsx": 2893,
"src/renderer/src/components/settings/accounts/AccountsPane.test.tsx": 2701,
"src/renderer/src/components/settings/agents/surfaces/AgentDefaultSurfaceSection.test.tsx": 3070,
"src/renderer/src/components/skills/setup/AgentSkillSetupPanel.freshness.test.tsx": 15418,
"src/renderer/src/components/skills/setup/AgentSkillSetupPanel.plan-status.test.tsx": 23334,
"src/renderer/src/components/skills/setup/AgentSkillSetupPanel.remote-availability.test.tsx": 5937,
@@ -1,28 +1,18 @@
// @vitest-environment happy-dom
import '@testing-library/jest-dom/vitest'
import { useEffect, useLayoutEffect, useState } from 'react'
import { act, cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
import { act, cleanup, fireEvent, render, screen, within } from '@testing-library/react'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { toast } from 'sonner'
import { i18n } from '@/i18n/i18n'
import esCatalog from '@/i18n/locales/es.json'
import { callAntigravityAccounts } from '@/runtime/runtime-antigravity-accounts-client'
import type { AntigravityAccountState } from '../../../../../shared/antigravity-account-types'
import { getDefaultSettings } from '../../../../../shared/constants'
import type { GlobalSettings } from '../../../../../shared/global-settings-types'
import type {
ClaudeManagedAccountSummary,
ClaudeRateLimitAccountsState
} from '../../../../../shared/managed-account-types'
import type { RuntimeClientTarget } from '../../../../../shared/runtime-client-target'
import { AccountsPane } from './AccountsPane'
import { createTestStore } from '@/store/slices/store-test-helpers'
import { useSettingsHostNavigation } from '../host-scope/use-settings-host-navigation'
import { resolveSettingsHostScope, type SettingsHostScope } from '../host-scope/settings-host-scope'
import type { SettingsHostScope } from '../host-scope/settings-host-scope'
import type { SettingsNavigationTarget } from '@/lib/settings-navigation-types'
import { ProviderAccountChatButton } from '../../status-bar/ProviderAccountChatButton'
import { ClaudeSwitcherMenu } from '../../status-bar/ClaudeSwitcherMenu'
import { TooltipProvider } from '../../ui/tooltip'
// Why: focus deliberately points at another server; the pane must follow hostScope only.
const focusedElsewhere = {
@@ -190,46 +180,6 @@ afterEach(() => {
Reflect.deleteProperty(window, 'api')
})
it('lists the accounts of the host chosen in the page, not the focused server', async () => {
render(renderPane(scopeFor(null)))
await act(async () => {})
expect(screen.getByText('local@desktop.test')).toBeTruthy()
expect(fake.watchedTargets).toEqual([{ kind: 'local' }])
})
it('shows Windows account guidance for the page owner rather than the focused server', async () => {
fake.query = 'antigravity'
const empty: AntigravityAccountState = {
accounts: [],
activeAccountId: null,
selectedAccountId: null,
currentAccount: null
}
vi.mocked(callAntigravityAccounts).mockResolvedValue(empty)
render(
<AccountsPane
settings={focusedElsewhere}
updateSettings={vi.fn()}
updateSettingsOrThrow={vi.fn()}
hostScope={scopeFor('server-a')}
accountOwnerPlatform="win32"
/>
)
expect(await screen.findByText('No native agy account is signed in.')).toBeTruthy()
expect(callAntigravityAccounts).toHaveBeenCalledWith(
{ kind: 'environment', environmentId: 'server-a' },
{ runtime: 'host', wslDistro: null },
'List'
)
expect(
screen.getByText(
'Select applies and verifies the host default agy credential. Command choices and later account changes are handled by agy. Existing sessions may keep their previous account.'
)
).toBeTruthy()
expect(screen.queryByText(/Selection applies to new agy sessions on this host/)).toBeNull()
})
it("drops a server's late account result after the page moves to another server", async () => {
const view = render(renderPane(scopeFor('server-a')))
await act(async () => {})
@@ -263,237 +213,3 @@ it('keeps a late account result once the pane is back on the server it was sent
expect(within(alphaRow()).getByText('Active')).toBeTruthy()
})
it("shows a WSL host's distro accounts from this desktop and selects for that distro", async () => {
const roster = claudeRoster('acct-w', 'wsl@ubuntu.test', false)
roster.accounts = roster.accounts.map((account) => ({
...account,
managedAuthRuntime: 'wsl',
wslDistro: 'Ubuntu'
}))
fake.rosters.set('local', roster)
render(
<AccountsPane
settings={focusedElsewhere}
updateSettings={vi.fn()}
updateSettingsOrThrow={vi.fn()}
hostScope={scopeFor(null)}
wslHostDistro="Ubuntu"
wslSupportedPlatform
wslAvailable
wslDistros={['Ubuntu']}
/>
)
await act(async () => {})
expect(fake.watchedTargets).toEqual([{ kind: 'local' }])
fireEvent.click(screen.getByText('wsl@ubuntu.test'))
expect(fake.selects).toEqual([
[{ kind: 'local' }, { accountId: 'acct-w', runtime: 'wsl', wslDistro: 'Ubuntu' }]
])
})
it.each(['add', 'manage'] as const)(
'%s navigation keeps native Windows accounts through consumption until explicit navigation releases them',
async (entry) => {
vi.spyOn(navigator, 'userAgent', 'get').mockReturnValue('Windows')
const roster = claudeRoster('native', 'native@windows.test', true)
roster.accounts.push(
{ ...roster.accounts[0], id: 'native2', email: 'second@windows.test' },
{
...roster.accounts[0],
id: 'wsl',
email: 'wsl@ubuntu.test',
managedAuthRuntime: 'wsl',
wslDistro: 'Ubuntu'
}
)
fake.rosters.set('local', roster)
const settingsStore = createTestStore()
settingsStore.setState({
settings: {
...focusedElsewhere,
localAccountRuntime: 'wsl',
localAccountWslDistro: 'Ubuntu'
}
})
const writeSettings = vi.fn(async (updates: Partial<GlobalSettings>) => ({
...settingsStore.getState().settings,
...updates
}))
Object.assign(window.api, { settings: { set: writeSettings } })
function Visit({
target,
clear
}: {
target: SettingsNavigationTarget | null
clear: () => void
}) {
const navigation = useSettingsHostNavigation(target)
const { consumeSettingsNavigation } = navigation
const settings = settingsStore((state) => state.settings) ?? focusedElsewhere
const scope = resolveSettingsHostScope({
choice: navigation.settingsHostChoice,
defaultEnvironmentId: 'focused-server',
savedEnvironmentIds: ['focused-server'],
catalogHydrated: true
})
useEffect(() => {
if (target) {
consumeSettingsNavigation(target)
clear()
}
}, [target, clear, consumeSettingsNavigation])
useLayoutEffect(() => {
// The first possible action precedes consumption effects, even with another default host.
document.querySelector<HTMLButtonElement>('#accounts-claude button')?.click()
}, [])
return (
<>
<button onClick={() => navigation.setSettingsHostChoice({ kind: 'local' })}>
Choose local host
</button>
<button
onClick={() => navigation.consumeSettingsNavigation({ pane: 'accounts', repoId: null })}
>
Ordinary accounts
</button>
<AccountsPane
settings={settings}
updateSettings={settingsStore.getState().updateSettings}
updateSettingsOrThrow={settingsStore.getState().updateSettingsOrThrow}
hostScope={scope}
nativeAccounts={navigation.nativeAccounts}
onAccountLocationChange={navigation.releaseNativeAccounts}
wslSupportedPlatform
wslAvailable
wslDistros={['Ubuntu']}
/>
</>
)
}
function Flow() {
const [target, setTarget] = useState<SettingsNavigationTarget | null>(null)
const [opened, setOpened] = useState(false)
fake.navigate = (next) => {
setTarget(next)
setOpened(true)
}
return (
<TooltipProvider>
{opened ? (
<>
<button onClick={() => setOpened(false)}>Close Settings</button>
<Visit target={target} clear={() => setTarget(null)} />
</>
) : entry === 'add' ? (
<ProviderAccountChatButton
provider="claude"
target={{ kind: 'local' }}
add
disabled={false}
/>
) : (
<ClaudeSwitcherMenu
claude={null}
compact
iconOnly={false}
chatAction={{ target: { kind: 'local' }, canSwitch: async () => true }}
/>
)}
</TooltipProvider>
)
}
const view = render(<Flow />)
const enter = async () => {
const trigger = await screen.findByRole('button', {
name: entry === 'add' ? 'Add Account…' : 'Switch Account'
})
await waitFor(() => expect(trigger).toBeEnabled())
if (entry === 'add') {
fireEvent.click(trigger)
} else {
fireEvent.pointerDown(trigger, { button: 0, ctrlKey: false, pointerType: 'mouse' })
fireEvent.click(await screen.findByRole('menuitem', { name: /Add another account…/ }))
}
}
const add = async () => {
const section = document.getElementById('accounts-claude')
if (!section) {
throw new Error('Missing Claude section')
}
const button = within(section).getByRole('button', { name: 'Add Account' })
await waitFor(() => expect(button).toBeEnabled())
fireEvent.click(button)
await act(async () => {})
}
await enter()
await waitFor(() => expect(fake.add).toHaveBeenCalled())
expect(fake.add.mock.calls[0]).toEqual([
{ runtime: 'host', wslDistro: undefined, copyLink: false }
])
expect(fake.watchedTargets).not.toContainEqual({
kind: 'environment',
environmentId: 'focused-server'
})
expect(screen.getByText('native@windows.test')).toBeTruthy()
expect(screen.queryByText('wsl@ubuntu.test')).toBeNull()
expect(writeSettings).not.toHaveBeenCalled()
await add()
expect(fake.add.mock.lastCall?.[0].runtime).toBe('host')
// The actual picker remains available; only an explicit selection writes the preference.
fake.query = 'Account Location'
view.rerender(<Flow />)
vi.spyOn(console, 'error').mockImplementation(() => {})
i18n.addResourceBundle('es', 'translation', esCatalog)
await i18n.changeLanguage(entry === 'manage' ? 'es' : 'en')
writeSettings.mockRejectedValueOnce(new Error('settings write failed'))
await act(async () => fireEvent.click(screen.getByRole('radio', { name: 'Windows' })))
expect(toast.error).toHaveBeenCalledExactlyOnceWith(
entry === 'manage'
? 'No se pudo guardar la ubicación de las cuentas. Inténtalo de nuevo.'
: 'Account location could not be saved. Try again.'
)
expect(console.error).not.toHaveBeenCalled()
await i18n.changeLanguage('en')
expect(settingsStore.getState().settings?.localAccountRuntime).toBe('wsl')
expect(screen.getByRole('radio', { name: 'Windows' })).toHaveAttribute('aria-checked', 'true')
fake.query = 'claude'
view.rerender(<Flow />)
expect(screen.getByText('native@windows.test')).toBeTruthy()
expect(screen.queryByText('wsl@ubuntu.test')).toBeNull()
fake.query = 'Account Location'
view.rerender(<Flow />)
writeSettings.mockClear()
const write = Promise.withResolvers<GlobalSettings>()
writeSettings.mockReturnValueOnce(write.promise)
fireEvent.click(screen.getByRole('radio', { name: 'Windows' }))
expect(screen.getByRole('radio', { name: 'Windows' })).toHaveAttribute('aria-checked', 'true')
expect(writeSettings).toHaveBeenCalledExactlyOnceWith({ localAccountRuntime: 'host' })
await act(async () =>
write.resolve({
...focusedElsewhere,
localAccountRuntime: 'host',
localAccountWslDistro: 'Ubuntu'
})
)
await act(async () => fireEvent.click(screen.getByRole('radio', { name: 'WSL' })))
expect(writeSettings.mock.lastCall).toEqual([{ localAccountRuntime: 'wsl' }])
fake.query = 'claude'
view.rerender(<Flow />)
await add()
expect(fake.add.mock.lastCall?.[0]).toMatchObject({ runtime: 'wsl', wslDistro: 'Ubuntu' })
expect(screen.getByText('wsl@ubuntu.test')).toBeTruthy()
fireEvent.click(screen.getByText('Close Settings'))
await enter()
await add()
expect(fake.add.mock.lastCall?.[0].runtime).toBe('host')
fireEvent.click(screen.getByText('Choose local host'))
await add()
expect(fake.add.mock.lastCall?.[0].runtime).toBe('wsl')
fireEvent.click(screen.getByText('Close Settings'))
await enter()
fireEvent.click(screen.getByText('Ordinary accounts'))
await add()
expect(fake.add.mock.lastCall?.[0].runtime).toBe('wsl')
}
)
@@ -1,295 +0,0 @@
// @vitest-environment happy-dom
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { getDefaultSettings } from '../../../../../shared/constants'
import { AccountsPane } from './AccountsPane'
import { LOCAL_SETTINGS_HOST_SCOPE } from '../host-scope/settings-host-scope'
const fake = vi.hoisted(() => ({
query: '',
cursorStatus: vi.fn(async () => ({
signedIn: false,
email: null,
displayName: null,
credentialSource: null,
planType: null,
tokenFresh: false,
error: null
})),
cursorRefresh: vi.fn(async () => {}),
cursorUsage: { updatedAt: 0 },
status: vi.fn(async () => ({
signedIn: false,
email: null,
teamId: null,
tokenFresh: false,
error: null
})),
pending: vi.fn(async () => null),
subscribe: vi.fn(() => vi.fn()),
refresh: vi.fn(async () => {}),
write: vi.fn(),
watcher: vi.fn(() => ({ close: vi.fn() })),
grokUsage: { updatedAt: 0 }
}))
vi.mock('@/i18n/i18n', () => ({
i18n: { language: 'en' },
translate: (_key: string, fallback: string, values?: Record<string, string | number>) =>
Object.entries(values ?? {}).reduce(
(text, [key, value]) => text.replaceAll(`{{${key}}}`, String(value)),
fallback
)
}))
vi.mock('@/store/index', () => ({
useAppStore: (selector: (state: Record<string, unknown>) => unknown) =>
selector({
settingsSearchQuery: fake.query,
rateLimits: {
codex: null,
codexTarget: { runtime: 'host', wslDistro: null },
minimax: null,
cursor: fake.cursorUsage,
grok: fake.grokUsage
},
runtimeEnvironments: [],
refreshRateLimits: fake.cursorRefresh,
refreshGrokRateLimits: fake.refresh,
recordFeatureInteraction: fake.write,
fetchSettings: fake.write
})
}))
vi.mock('@/runtime/runtime-provider-accounts-client', () => ({
emptyClaudeAccountsState: () => ({
accounts: [],
activeAccountId: null,
activeAccountIdsByRuntime: { host: null, wsl: {} }
}),
emptyCodexAccountsState: () => ({
accounts: [],
activeAccountId: null,
activeAccountIdsByRuntime: { host: null, wsl: {} }
}),
getProviderAccountsOwnerKey: (target: { kind: string; environmentId?: string }) =>
target.environmentId ?? 'local',
watchProviderAccounts: fake.watcher,
selectClaudeProviderAccount: fake.write,
selectCodexProviderAccount: fake.write,
removeClaudeProviderAccount: fake.write,
removeCodexProviderAccount: fake.write
}))
beforeEach(() => {
vi.clearAllMocks()
fake.query = ''
fake.grokUsage = { updatedAt: 0 }
fake.cursorUsage = { updatedAt: 0 }
Object.assign(window, {
api: {
opencodeGoCredentials: {
getStatus: vi.fn(async () => ({ apiKeyConfigured: false })),
saveApiKey: vi.fn(async () => ({ apiKeyConfigured: true })),
clearApiKey: vi.fn(async () => ({ apiKeyConfigured: false }))
},
minimaxCredentials: {
getStatus: vi.fn(async () => ({ cookieConfigured: false, apiKeyConfigured: false }))
},
zcodePlanCredentials: {
getStatus: vi.fn(async () => ({
apiKeyConfigured: false,
zcodeCliConfigured: false,
apiKeyProtection: null
}))
},
codexConfigSync: {
status: vi.fn(async () => ({
state: 'synced',
reason: null,
systemConfigPath: '/synthetic/config.toml'
}))
},
codexAccounts: { getPendingLoginUrl: fake.pending, onPendingLoginUrlChanged: fake.subscribe },
cursorAccounts: { getStatus: fake.cursorStatus },
grokAccounts: { getStatus: fake.status }
}
})
})
afterEach(() => {
cleanup()
Reflect.deleteProperty(window, 'api')
})
it.each([false, true])(
'retains Grok status reads with Windows account support %s',
async (wslSupportedPlatform) => {
const props = {
settings: getDefaultSettings('/synthetic'),
updateSettings: fake.write,
updateSettingsOrThrow: fake.write,
hostScope: LOCAL_SETTINGS_HOST_SCOPE,
wslSupportedPlatform
}
const view = render(<AccountsPane {...props} />)
await act(async () => {})
// Assert DOM identity across query edits so search cannot remount sections and add reads/watchers.
const mountedHeading = screen.getByText('Grok (xAI)', { exact: true })
const watchersAfterMount = fake.watcher.mock.calls.length
const statusReadsAfterMount = fake.status.mock.calls.length
for (const query of ['g', 'gr', 'gro', 'grok', '', 'g', 'gr', 'gro', 'grok']) {
fake.query = query
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(screen.getByText('Grok (xAI)', { exact: true })).toBe(mountedHeading)
}
expect(fake.watcher.mock.calls.length).toBe(watchersAfterMount)
expect(fake.status.mock.calls.length).toBe(statusReadsAfterMount)
}
)
it.each([false, true])(
'retains Codex login reads with Windows account support %s',
async (wslSupportedPlatform) => {
const props = {
settings: getDefaultSettings('/synthetic'),
updateSettings: fake.write,
updateSettingsOrThrow: fake.write,
hostScope: LOCAL_SETTINGS_HOST_SCOPE,
wslSupportedPlatform
}
const view = render(<AccountsPane {...props} />)
await act(async () => {})
const mountedHeading = screen.getByText('Codex', { exact: true })
const pendingReadsAfterMount = fake.pending.mock.calls.length
const subscriptionsAfterMount = fake.subscribe.mock.calls.length
for (const query of [
'c',
'co',
'cod',
'code',
'codex',
'',
'c',
'co',
'cod',
'code',
'codex'
]) {
fake.query = query
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(screen.getByText('Codex', { exact: true })).toBe(mountedHeading)
}
expect(fake.pending.mock.calls.length).toBe(pendingReadsAfterMount)
expect(fake.subscribe.mock.calls.length).toBe(subscriptionsAfterMount)
}
)
it('preserves explicit Grok refresh and real hide/reopen status reads', async () => {
fake.query = 'grok'
const props = {
settings: getDefaultSettings('/synthetic'),
updateSettings: fake.write,
updateSettingsOrThrow: fake.write,
hostScope: LOCAL_SETTINGS_HOST_SCOPE
}
const view = render(<AccountsPane {...props} />)
await act(async () => {})
fireEvent.click(screen.getByRole('button', { name: 'Refresh usage' }))
await act(async () => {})
expect(fake.refresh).toHaveBeenCalledTimes(1)
expect(fake.status).toHaveBeenCalledTimes(2)
fake.query = 'codex'
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(screen.queryByText('Grok (xAI)', { exact: true })).toBeNull()
fake.query = 'grok'
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(fake.status).toHaveBeenCalledTimes(3)
expect(fake.write).not.toHaveBeenCalled()
})
it('keeps usage-driven Grok status refreshes while the section stays mounted', async () => {
fake.query = 'grok'
const props = {
settings: getDefaultSettings('/synthetic'),
updateSettings: fake.write,
updateSettingsOrThrow: fake.write,
hostScope: LOCAL_SETTINGS_HOST_SCOPE
}
const view = render(<AccountsPane {...props} />)
await act(async () => {})
fake.grokUsage = { updatedAt: 10 }
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(fake.status).toHaveBeenCalledTimes(2)
fake.query = 'gro'
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(fake.status).toHaveBeenCalledTimes(2)
fake.grokUsage = { updatedAt: 11 }
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(fake.status).toHaveBeenCalledTimes(3)
})
it('keeps the login subscription until Codex genuinely hides', async () => {
const props = {
settings: getDefaultSettings('/synthetic'),
updateSettings: fake.write,
updateSettingsOrThrow: fake.write,
hostScope: LOCAL_SETTINGS_HOST_SCOPE
}
const view = render(<AccountsPane {...props} />)
await act(async () => {})
const unsubscribe = fake.subscribe.mock.results[0].value
fake.query = 'codex'
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(unsubscribe).not.toHaveBeenCalled()
fake.query = 'grok'
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(unsubscribe).toHaveBeenCalledTimes(1)
fake.query = 'codex'
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(fake.subscribe).toHaveBeenCalledTimes(2)
view.unmount()
expect(fake.subscribe.mock.results[1].value).toHaveBeenCalledTimes(1)
})
it.each([false, true])(
'retains Cursor status reads with Windows account support %s',
async (wslSupportedPlatform) => {
const props = {
settings: getDefaultSettings('/synthetic'),
updateSettings: fake.write,
updateSettingsOrThrow: fake.write,
hostScope: LOCAL_SETTINGS_HOST_SCOPE,
wslSupportedPlatform
}
const view = render(<AccountsPane {...props} />)
await act(async () => {})
const mountedHeading = screen.getByText('Cursor', { exact: true })
const statusReadsAfterMount = fake.cursorStatus.mock.calls.length
for (const query of ['c', 'cu', 'cur', 'curs', 'curso', 'cursor', '', 'c', 'cu', 'cursor']) {
fake.query = query
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(screen.getByText('Cursor', { exact: true })).toBe(mountedHeading)
}
expect(fake.cursorStatus.mock.calls.length).toBe(statusReadsAfterMount)
expect(fake.write).not.toHaveBeenCalled()
}
)
it('keeps Cursor refresh driven by usage updates and genuine reopen', async () => {
fake.query = 'cursor'
const props = {
settings: getDefaultSettings('/synthetic'),
updateSettings: fake.write,
updateSettingsOrThrow: fake.write,
hostScope: LOCAL_SETTINGS_HOST_SCOPE
}
const view = render(<AccountsPane {...props} />)
await act(async () => {})
fireEvent.click(screen.getByRole('button', { name: 'Refresh usage' }))
await act(async () => {})
expect(fake.cursorRefresh).toHaveBeenCalledTimes(1)
expect(fake.cursorStatus).toHaveBeenCalledTimes(1)
fake.cursorUsage = { updatedAt: 1 }
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(fake.cursorStatus).toHaveBeenCalledTimes(2)
fake.query = 'codex'
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(screen.queryByText('Cursor', { exact: true })).toBeNull()
fake.query = 'cursor'
await act(async () => view.rerender(<AccountsPane {...props} />))
expect(fake.cursorStatus).toHaveBeenCalledTimes(3)
})
@@ -1,319 +0,0 @@
import React from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { getDefaultSettings } from '../../../../../shared/constants'
import type { GlobalSettings } from '../../../../../shared/global-settings-types'
import type {
ClaudeManagedAccountSummary,
ClaudeRateLimitAccountsState
} from '../../../../../shared/managed-account-types'
import { i18n } from '../../../i18n/i18n'
import { useAppStore } from '../../../store/index'
import { AccountsPane } from './AccountsPane'
import { resolveSettingsHostScope } from '../host-scope/settings-host-scope'
import { renderClaudeAccountsSection } from './claude/accounts-pane-claude-section'
import type { AccountsPaneSectionModel } from './accounts-pane-types'
function renderPane(
settings: GlobalSettings,
props: Partial<React.ComponentProps<typeof AccountsPane>> = {}
): string {
return renderToStaticMarkup(
React.createElement(AccountsPane, {
settings,
updateSettings: vi.fn(),
updateSettingsOrThrow: vi.fn(),
hostScope: resolveSettingsHostScope({
choice: null,
defaultEnvironmentId: settings.activeRuntimeEnvironmentId,
savedEnvironmentIds: [],
catalogHydrated: false
}),
...props
})
)
}
describe('AccountsPane', () => {
beforeEach(async () => {
await i18n.changeLanguage('en')
useAppStore.setState({ settingsSearchQuery: '', runtimeEnvironments: [] })
})
it('hides the WSL account location controls on platforms without WSL support', () => {
const markup = renderPane({
...getDefaultSettings('/tmp'),
localAccountRuntime: 'wsl'
})
expect(markup).not.toContain('Account location')
expect(markup).not.toContain('aria-label="Account location"')
expect(markup).not.toContain('WSL is not available on this machine.')
})
it('keeps the WSL account location controls on Windows-class hosts', () => {
const markup = renderPane(
{
...getDefaultSettings('/tmp'),
localAccountRuntime: 'wsl'
},
{ wslSupportedPlatform: true, wslCapabilitiesLoading: true }
)
expect(markup).toContain('Account location')
expect(markup).toContain('aria-label="Account location"')
expect(markup).toContain('role="radio" aria-checked="true" aria-disabled="true"')
})
it('selects the WSL account location under auto when the global project runtime is WSL', () => {
// Shadow the read-only userAgent prototype getter, then remove the own property to restore it.
const originalOwnUserAgent = Object.getOwnPropertyDescriptor(globalThis.navigator, 'userAgent')
Object.defineProperty(globalThis.navigator, 'userAgent', {
value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
configurable: true
})
try {
const markup = renderPane(
{
...getDefaultSettings('/tmp'),
localAccountRuntime: 'auto',
localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' }
},
{ wslSupportedPlatform: true, wslCapabilitiesLoading: true }
)
expect(markup).toContain('aria-label="Account location"')
expect(markup).toContain('role="radio" aria-checked="true" aria-disabled="true"')
} finally {
if (originalOwnUserAgent) {
Object.defineProperty(globalThis.navigator, 'userAgent', originalOwnUserAgent)
} else {
delete (globalThis.navigator as { userAgent?: string }).userAgent
}
}
})
it('keeps the runtime label inside the localized account copy', () => {
const markup = renderPane(getDefaultSettings('/tmp'))
expect(markup).toContain('Showing accounts for this device. New accounts are added there.')
expect(markup).toContain('authenticate with Google for this device. This uses credentials')
expect(markup).not.toContain('ShowingThis device')
expect(markup).not.toContain('forThis device')
})
it('localizes the runtime label before interpolating account copy', async () => {
await i18n.changeLanguage('es')
const markup = renderPane(getDefaultSettings('/tmp'))
expect(markup).toMatch(
/Mostrando cuentas para [Ee]ste dispositivo\. Las nuevas cuentas se agregan allí\./
)
expect(markup).not.toContain('This device')
})
it('scopes account copy to the active remote server and offers sign-in on that server', () => {
// SSR reads Zustand’s initial state; this covers unnamed pre-hydration scope, while provider-account-scope.test covers named servers.
const markup = renderPane(
{
...getDefaultSettings('/tmp'),
activeRuntimeEnvironmentId: 'env-1'
},
{ wslSupportedPlatform: true }
)
expect(
markup.split(
'Showing accounts managed by the remote server. New accounts sign in there; you finish in this device’s browser.'
).length - 1
).toBe(2)
// Both sections must reassure desktop users and link runtime selection so remote scope cannot look like lost accounts.
expect(markup.split('Accounts managed on this desktop are unchanged').length - 1).toBe(2)
expect(markup.split('Open Remote Servers').length - 1).toBe(2)
// Unloaded server names must not interpolate prose fallbacks into scope labels.
expect(markup).toContain('Account scope: Remote server<')
expect(markup).not.toContain('Remote server: the remote server')
expect(markup).not.toContain('aria-label="Account location"')
const addButtons = [...markup.matchAll(/Add Account/g)].map(({ index }) =>
markup.slice(markup.lastIndexOf('<button', index), index)
)
expect(addButtons).toHaveLength(2)
for (const button of addButtons) {
expect(button).not.toContain('disabled=""')
}
expect(markup).not.toContain('More sign-in options')
})
it('omits the scope control on the web client, which cannot select Local desktop', () => {
const webGlobal = globalThis as { window?: { __ORCA_WEB_CLIENT__?: boolean } }
const hadWindow = 'window' in webGlobal
webGlobal.window = { ...webGlobal.window, __ORCA_WEB_CLIENT__: true }
try {
const markup = renderPane({
...getDefaultSettings('/tmp'),
activeRuntimeEnvironmentId: 'env-1'
})
// Web clients cannot return to desktop-owned accounts, so that promise would be misleading.
expect(markup).not.toContain('Accounts managed on this desktop are unchanged')
expect(markup).not.toContain('Open Remote Servers')
expect(markup).toContain('Showing accounts managed by')
} finally {
if (!hadWindow) {
delete webGlobal.window
}
}
})
it('keeps local copy and enabled sign-in actions when no remote server is active', () => {
const markup = renderPane(getDefaultSettings('/tmp'))
expect(markup).toContain('Showing accounts for this device. New accounts are added there.')
expect(markup).not.toContain('Open Remote Servers')
const addAccountIndex = markup.indexOf('Add Account')
expect(addAccountIndex).toBeGreaterThan(0)
expect(
markup.slice(markup.lastIndexOf('<button', addAccountIndex), addAccountIndex)
).not.toContain('disabled=""')
})
it('tells users to paste the OpenCode console session cookie, not auth alone', () => {
const markup = renderPane(getDefaultSettings('/tmp'))
expect(markup).toContain('__Host-console_session')
expect(markup).toContain('auth=…; __Host-console_session=…')
expect(markup).toContain('auth cookie still covers workspace discovery')
expect(markup).not.toContain('Fe26.2**… token or auth=Fe26.2**… header')
})
})
function account(
id: string,
email: string,
overrides: Partial<ClaudeManagedAccountSummary> = {}
): ClaudeManagedAccountSummary {
return {
id,
email,
managedAuthRuntime: 'host',
wslDistro: null,
authMethod: 'subscription-oauth',
organizationUuid: null,
organizationName: null,
createdAt: 1,
updatedAt: 1,
lastAuthenticatedAt: 1,
...overrides
}
}
function render(
state: Partial<ClaudeRateLimitAccountsState> = {},
settings: Partial<GlobalSettings> = {}
): string {
const claudeAccounts: ClaudeRateLimitAccountsState = {
accounts: [],
activeAccountId: null,
...state
}
const accountRuntime = { runtime: 'host' as const, wslDistro: null, label: 'This device' }
const model: AccountsPaneSectionModel = {
updateSettings: vi.fn(),
updateSettingsOrThrow: vi.fn(),
accountOwner: { kind: 'local' },
searchQuery: '',
recordFeatureInteraction: vi.fn(),
wslSupportedPlatform: false,
wslAvailable: false,
wslDistros: [],
localAccountRuntime: accountRuntime,
localAccountRuntimeSentenceLabel: 'this device',
remoteServerName: null,
codexAccounts: { accounts: [], activeAccountId: null },
codexAction: 'idle',
visibleCodexAccounts: [],
systemCodexActive: true,
systemCodexNeedsSignIn: false,
systemCodexMissingSignIn: false,
systemCodexIdentity: undefined,
activeCodexAuthWarning: null,
activeCodexAccountId: null,
codexConfigSync: null,
codexConfigSyncWarning: null,
codexRateLimits: null,
codexRateLimitTarget: { runtime: 'host', wslDistro: null },
setRemoveCodexTarget: vi.fn(),
runCodexAccountAction: vi.fn(async () => {}),
recordOpenCodeSettingEdit: vi.fn(),
accountRuntime,
accountRuntimeSentenceLabel: 'this device',
accountRuntimeUnavailable: false,
accountVisibilityOptions: { remoteOwner: false, ownerPlatform: 'darwin' },
claudeAccounts,
claudeAction: 'idle',
isRemoteAccountScope: false,
remoteAccountScopeNotice: null,
runClaudeAccountAction: vi.fn(async () => {}),
setRemoveClaudeTarget: vi.fn(),
settings: { ...getDefaultSettings('/tmp'), ...settings },
systemClaudeActive: claudeAccounts.activeAccountId === null,
visibleClaudeAccounts: claudeAccounts.accounts,
wslCapabilitiesLoading: false
}
return renderToStaticMarkup(React.createElement(() => renderClaudeAccountsSection(model)))
}
function selectButtons(markup: string): string[] {
return [
...markup.matchAll(/<button type="button"[^>]*class="flex min-w-0 flex-1[^"]*"[^>]*>/g)
].map(([button]) => button)
}
describe('Claude accounts section', () => {
beforeEach(async () => {
await i18n.changeLanguage('en')
useAppStore.setState({ settingsSearchQuery: '', runtimeEnvironments: [] })
})
it('asks a folder with no login to sign in again, and keeps it unselectable but removable', () => {
const markup = render({
accounts: [
account('legacy', 'old@example.test', { needsSignIn: true }),
account('ready', 'ok@example.test')
]
})
expect(markup).toContain('Sign in again to use this account')
expect(selectButtons(markup).map((button) => button.includes('disabled=""'))).toEqual([
true,
false
])
expect(markup.match(/>Remove<\/button>/g)).toHaveLength(2)
})
it("names System default's login and warns only when an older Orca may have copied it there", () => {
const saved = {
accounts: [account('a', 'A@example.test')],
systemDefaultEmail: 'a@example.test'
}
const copied = render({ ...saved, systemDefaultMayBeCopied: true })
expect(copied).toContain('System default: a@example.test')
expect(copied).toContain('An earlier Orca version may have copied that login there.')
expect(copied).toContain('>Dismiss</button>')
// Saving your own login as an account too is normal: no copy evidence, no warning.
expect(render(saved)).not.toContain('An earlier Orca version')
expect(
render(
{ ...saved, systemDefaultMayBeCopied: true },
{ claudeCopiedSystemDefaultNoticeDismissed: true }
)
).not.toContain('An earlier Orca version')
const own = render({
...saved,
systemDefaultEmail: 'me@example.test',
systemDefaultMayBeCopied: true
})
expect(own).toContain('System default: me@example.test')
expect(own).not.toContain('An earlier Orca version')
})
})
@@ -1,7 +1,7 @@
// @vitest-environment happy-dom
import '@testing-library/jest-dom/vitest'
import { cleanup, render, screen, waitFor } from '@testing-library/react'
import { cleanup, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { AntigravityAccountsSection } from './AntigravityAccountsSection'
@@ -76,72 +76,6 @@ beforeEach(() => {
afterEach(() => cleanup())
describe('native Antigravity Accounts', () => {
it.each([
{ owner, target, ownerPlatform: 'win32', windows: true },
{
owner: { kind: 'environment', environmentId: 'remote' } as const,
target,
ownerPlatform: 'linux',
windows: false
},
{ owner, target, ownerPlatform: 'darwin', windows: false },
{ owner, target: { runtime: 'wsl' } as const, ownerPlatform: 'win32', windows: false }
])('describes the owning host launch promise (%#)', async ({ windows, ...props }) => {
vi.mocked(callAntigravityAccounts).mockResolvedValue(changedState)
render(<AntigravityAccountsSection {...props} label="This host" />)
await screen.findByText('second@example.invalid')
if (windows) {
expect(
screen.getByText(
'The host default account changed. Select a saved account to apply it again.'
)
).toBeInTheDocument()
expect(
screen.getByText(/Select applies and verifies the host default agy credential/)
).toHaveTextContent('Command choices and later account changes are handled by agy.')
expect(screen.queryByText(/before launching agy/)).toBeNull()
} else {
expect(
screen.getByText(/Select a saved account again before launching agy/)
).toBeInTheDocument()
expect(
screen.getByText(/Selection applies to new agy sessions on this host/)
).toBeInTheDocument()
}
})
it('saves a signed-in account with unknown identity and displays the confirmed account', async () => {
const save = Promise.withResolvers<AntigravityAccountState>()
vi.mocked(callAntigravityAccounts)
.mockResolvedValueOnce(unidentifiedState)
.mockReturnValueOnce(save.promise)
render(<AntigravityAccountsSection owner={owner} target={target} label="This device" />)
await screen.findByText('Signed-in identity unavailable')
await userEvent.click(screen.getByRole('button', { name: 'Save current account' }))
expect(screen.getByRole('button', { name: 'Save current account' })).toBeDisabled()
save.resolve({ ...state, selectedAccountId: null })
await screen.findByText('Native account')
expect(screen.getAllByText('synthetic@example.invalid')).toHaveLength(2)
})
it.each(['Refresh accounts', 'Save current account'])(
'hides the previous account quota when %s observes a different native identity',
async (action) => {
vi.mocked(callRuntimeRpc).mockResolvedValue(usage)
render(<AntigravityAccountsSection owner={owner} target={target} label="This device" />)
await screen.findByText('Native account')
await userEvent.click(screen.getByRole('button', { name: 'Refresh usage' }))
await screen.findByText('Session: 31% · Weekly: —')
vi.mocked(callAntigravityAccounts).mockResolvedValueOnce(changedState)
await userEvent.click(screen.getByRole('button', { name: action }))
await screen.findByText('second@example.invalid')
expect(screen.queryByText('Session: 31% · Weekly: —')).toBeNull()
await userEvent.click(screen.getByRole('button', { name: 'Refresh accounts' }))
await screen.findByText('Native account')
expect(screen.queryByText('Session: 31% · Weekly: —')).toBeNull()
}
)
it.each([
{ change: 'a known identity changes', before: state, after: changedState },
{ change: 'a known identity becomes unknown', before: state, after: unidentifiedState },
@@ -177,78 +111,6 @@ describe('native Antigravity Accounts', () => {
expect(screen.queryByText('Session: 31% · Weekly: —')).toBeNull()
})
it.each(['Refresh accounts', 'Save current account'])(
'shows unidentified usage until %s rechecks credentials',
async (action) => {
vi.mocked(callAntigravityAccounts).mockResolvedValue(unidentifiedState)
vi.mocked(callRuntimeRpc).mockResolvedValue(usage)
render(<AntigravityAccountsSection owner={owner} target={target} label="This device" />)
await screen.findByText('Signed-in identity unavailable')
await userEvent.click(screen.getByRole('button', { name: 'Refresh usage' }))
await screen.findByText('Session: 31% · Weekly: —')
expect(screen.queryByRole('alert')).toBeNull()
if (action === 'Save current account') {
vi.mocked(callAntigravityAccounts).mockRejectedValueOnce(new Error('Identity unavailable'))
}
await userEvent.click(screen.getByRole('button', { name: action }))
await waitFor(() => expect(screen.getByRole('button', { name: action })).toBeEnabled())
expect(screen.queryByText('Session: 31% · Weekly: —')).toBeNull()
}
)
it('shows the native identity and prevents removing the active account', async () => {
render(<AntigravityAccountsSection owner={owner} target={target} label="This device" />)
await screen.findByText('Native account')
expect(screen.getAllByText('synthetic@example.invalid')).toHaveLength(2)
expect(screen.getByRole('button', { name: 'Remove' }).hasAttribute('disabled')).toBe(true)
})
it('keeps account identity visible when quota refresh fails', async () => {
vi.mocked(callRuntimeRpc).mockRejectedValue(new Error('Quota is unavailable'))
render(<AntigravityAccountsSection owner={owner} target={target} label="This device" />)
await screen.findByText('Native account')
await userEvent.click(screen.getByRole('button', { name: 'Refresh usage' }))
expect(await screen.findByRole('alert')).toHaveTextContent('Quota is unavailable')
expect(screen.getByText('Native account')).toBeTruthy()
expect(screen.getAllByText('synthetic@example.invalid')).toHaveLength(2)
})
it('forwards the selected owning host and distro, and displays refusal without local fallback', async () => {
vi.mocked(callAntigravityAccounts).mockRejectedValue(new Error('Unsupported owning distro'))
render(
<AntigravityAccountsSection
owner={{ kind: 'environment', environmentId: 'remote' }}
target={{ runtime: 'wsl', wslDistro: 'Ubuntu' }}
label="Remote Ubuntu"
/>
)
expect(await screen.findByRole('alert')).toHaveTextContent('Unsupported owning distro')
expect(callAntigravityAccounts).toHaveBeenCalledWith(
{ kind: 'environment', environmentId: 'remote' },
{ runtime: 'wsl', wslDistro: 'Ubuntu' },
'List'
)
expect(screen.queryByRole('button', { name: 'Save current account' })).toBeNull()
})
it('disables account controls synchronously during a selection and never claims success on conflict', async () => {
const deferred = Promise.withResolvers<AntigravityAccountState>()
render(<AntigravityAccountsSection owner={owner} target={target} label="This device" />)
await screen.findByText('Native account')
vi.mocked(callAntigravityAccounts).mockReturnValueOnce(deferred.promise)
await userEvent.click(screen.getByRole('button', { name: 'Selected' }))
expect(
screen.getByRole('button', { name: 'Save current account' }).hasAttribute('disabled')
).toBe(true)
deferred.reject(new Error('Native conflict'))
expect(await screen.findByRole('alert')).toHaveTextContent('Native conflict')
await waitFor(() =>
expect(
screen.getByRole('button', { name: 'Save current account' }).hasAttribute('disabled')
).toBe(false)
)
})
it('re-reads native identity after an unsuccessful selection rather than retaining stale native state', async () => {
render(<AntigravityAccountsSection owner={owner} target={target} label="This device" />)
await screen.findByText('Native account')
@@ -1,264 +0,0 @@
// @vitest-environment happy-dom
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { toast } from 'sonner'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createGlobalSettingsFixture } from '../../../../../../shared/global-settings-test-fixture'
import type { GlobalSettings } from '../../../../../../shared/global-settings-types'
import { track } from '@/lib/telemetry'
import { useAppStore } from '@/store/index'
import { saveNativeChatProviderChoice } from '../../../../lib/agent-session/launch/native-chat-provider-choice'
import { AgentDefaultSurfaceSection } from './AgentDefaultSurfaceSection'
vi.mock('@/lib/chat-provider-agents', () => ({
chatProviderAgents: () => ['claude', 'codex', 'grok', 'opencode']
}))
vi.mock('@/lib/telemetry', () => ({ track: vi.fn() }))
vi.mock('sonner', () => ({ toast: { success: vi.fn(), dismiss: vi.fn(), error: vi.fn() } }))
let persisted: GlobalSettings
let presentation: 'tiles' | 'switch'
const settingsSet = vi.fn(async (updates: Partial<GlobalSettings>) => {
persisted = { ...persisted, ...updates }
return persisted
})
function Section(): React.JSX.Element | null {
const settings = useAppStore((state) => state.settings)
return settings ? (
<AgentDefaultSurfaceSection settings={settings} presentation={presentation} />
) : null
}
function renderSection(settings: Partial<GlobalSettings>): void {
persisted = createGlobalSettingsFixture(settings)
useAppStore.setState({ settings: persisted })
render(<Section />)
}
beforeEach(() => {
presentation = 'tiles'
useAppStore.setState(useAppStore.getInitialState(), true)
settingsSet.mockClear()
vi.mocked(track).mockClear()
vi.mocked(toast.success).mockClear()
vi.mocked(toast.error).mockClear()
Object.defineProperty(window, 'api', {
configurable: true,
value: { settings: { set: settingsSet } }
})
})
afterEach(cleanup)
describe('Open agents as', () => {
it('reuses the default write from the Chat UI button and retains per-agent choices', async () => {
presentation = 'switch'
renderSection({ agentDefaultSurface: 'terminal', nativeChatProviders: { claude: false } })
const control = screen.getByRole('button', { name: 'Turn on' })
expect(screen.getByText('Chat UI is off')).toBeTruthy()
expect(screen.getByText('New agents will launch as CLI by default.')).toBeTruthy()
fireEvent.click(control)
await waitFor(() => expect(screen.getByRole('button', { name: 'Turn off' })).toBeTruthy())
expect(settingsSet).toHaveBeenCalledExactlyOnceWith({ agentDefaultSurface: 'chat' })
expect(persisted.nativeChatProviders).toEqual({ claude: false })
expect(screen.getByText('Chat UI is on')).toBeTruthy()
expect(toast.success).toHaveBeenCalledExactlyOnceWith(
'Agents now open as chats',
expect.objectContaining({
description: 'Claude stays in the terminal, as you set.',
action: expect.objectContaining({ label: 'Set all to Chat' })
})
)
})
it('sets only the default, names agents set to the other surface, and Set all makes them follow', async () => {
renderSection({
agentDefaultSurface: 'terminal',
// Grok already matches Chat; OpenCode is off, so its row offers no choice.
nativeChatProviders: { claude: false, codex: false, grok: true, opencode: false },
disabledTuiAgents: ['opencode']
})
expect(screen.getByRole('radio', { name: /^Terminal/ })).toHaveProperty('checked', true)
fireEvent.click(screen.getByRole('radio', { name: /^Chat/ }))
await waitFor(() => expect(toast.success).toHaveBeenCalledTimes(1))
expect(settingsSet).toHaveBeenCalledExactlyOnceWith({ agentDefaultSurface: 'chat' })
const [title, options] = vi.mocked(toast.success).mock.calls[0]
expect(title).toBe('Agents now open as chats')
expect(options?.description).toBe('Claude and Codex stay in the terminal, as you set.')
const action = options?.action
if (!action || typeof action !== 'object' || !('onClick' in action)) {
throw new Error('Missing reset action')
}
expect(action.label).toBe('Set all to Chat')
fireEvent.click(
render(<button onClick={action.onClick}>Reset choices</button>).getByRole('button', {
name: 'Reset choices'
})
)
await waitFor(() => expect(settingsSet).toHaveBeenCalledTimes(2))
expect(settingsSet).toHaveBeenLastCalledWith({
nativeChatProviders: { grok: true, opencode: false }
})
})
it('shows no toast when every agent’s own choice already matches the new default', async () => {
renderSection({ agentDefaultSurface: 'chat', nativeChatProviders: { claude: false } })
fireEvent.click(screen.getByRole('radio', { name: /^Terminal/ }))
await waitFor(() =>
expect(settingsSet).toHaveBeenCalledExactlyOnceWith({ agentDefaultSurface: 'terminal' })
)
await Promise.resolve()
expect(toast.success).not.toHaveBeenCalled()
})
})
describe('Chat UI launch tip experiment', () => {
it.each([
[[], 'treatment', false],
[['chat-ui-here'], 'treatment', true],
[['chat-ui-here'], 'control', false]
] as const)(
'reports a switch to Chat with seen tips %j in %s as launch_tip_shown=%s',
async (seen, cohort, shown) => {
useAppStore.setState({ featureTipsSeenIds: [...seen] })
renderSection({
agentDefaultSurface: 'terminal',
telemetry: {
optedIn: true,
installId: 'install',
existedBeforeTelemetryRelease: true,
chatUiLaunchTipCohort: cohort
}
})
fireEvent.click(screen.getByRole('radio', { name: /^Chat/ }))
await waitFor(() =>
expect(track).toHaveBeenCalledExactlyOnceWith('agent_default_surface_changed', {
from_mode: 'terminal',
to_mode: 'chat',
source: 'settings',
launch_tip_shown: shown
})
)
}
)
})
describe('acknowledged default writes', () => {
it('reports a rejected tile save once, preserves the choice, and accepts its successor', async () => {
renderSection({ agentDefaultSurface: 'terminal' })
settingsSet.mockRejectedValueOnce(new Error('disk unavailable'))
fireEvent.click(screen.getByRole('radio', { name: /^Chat/ }))
await waitFor(() =>
expect(toast.error).toHaveBeenCalledExactlyOnceWith(
'Agent settings could not be saved. Try again.'
)
)
expect(persisted.agentDefaultSurface).toBe('terminal')
expect(screen.getByRole('radio', { name: /^Terminal/ })).toHaveProperty('checked', true)
expect(toast.success).not.toHaveBeenCalled()
fireEvent.click(screen.getByRole('radio', { name: /^Chat/ }))
await waitFor(() =>
expect(screen.getByRole('radio', { name: /^Chat/ })).toHaveProperty('checked', true)
)
expect(persisted.agentDefaultSurface).toBe('chat')
expect(toast.error).toHaveBeenCalledTimes(1)
})
it('disables both tiles with pending feedback until the checked write settles', async () => {
renderSection({ agentDefaultSurface: 'terminal' })
let release: () => void = () => {}
settingsSet.mockImplementationOnce(async (updates) => {
await new Promise<void>((resolve) => {
release = resolve
})
persisted = { ...persisted, ...updates }
return persisted
})
fireEvent.click(screen.getByRole('radio', { name: /^Chat/ }))
await waitFor(() => expect(settingsSet).toHaveBeenCalledTimes(1))
expect(screen.getByRole('radiogroup').getAttribute('aria-busy')).toBe('true')
for (const radio of screen.getAllByRole('radio')) {
expect(radio).toHaveProperty('disabled', true)
}
expect(screen.getByRole('radio', { name: /^Terminal/ })).toHaveProperty('checked', true)
await act(async () => release())
await waitFor(() =>
expect(screen.getByRole('radio', { name: /^Chat/ })).toHaveProperty('checked', true)
)
expect(screen.getByRole('radiogroup').getAttribute('aria-busy')).toBe('false')
fireEvent.click(screen.getByRole('radio', { name: /^Terminal/ }))
await waitFor(() => expect(persisted.agentDefaultSurface).toBe('terminal'))
})
it('reports reset rejection once without losing overrides and continues the shared per-agent queue', async () => {
renderSection({
agentDefaultSurface: 'terminal',
nativeChatProviders: { claude: false, codex: false, custom: true }
})
fireEvent.click(screen.getByRole('radio', { name: /^Chat/ }))
await waitFor(() => expect(toast.success).toHaveBeenCalledTimes(1))
settingsSet.mockRejectedValueOnce(new Error('disk unavailable'))
const action = vi.mocked(toast.success).mock.calls[0][1]?.action
if (!action || typeof action !== 'object' || !('onClick' in action)) {
throw new Error('Missing reset action')
}
fireEvent.click(
render(<button onClick={action.onClick}>Reset choices</button>).getByRole('button', {
name: 'Reset choices'
})
)
await waitFor(() => expect(toast.error).toHaveBeenCalledTimes(1))
expect(persisted.nativeChatProviders).toEqual({ claude: false, codex: false, custom: true })
await act(async () =>
saveNativeChatProviderChoice({
agent: 'codex',
on: true,
fallbackSettings: persisted,
updateSettings: useAppStore.getState().updateSettingsOrThrow
})
)
expect(persisted.nativeChatProviders).toEqual({ claude: false, custom: true })
expect(toast.error).toHaveBeenCalledTimes(1)
})
})
describe('notice and reset work bounds', () => {
it.each([20, 200])(
'scans %i saved choices linearly with one write per user action',
async (count) => {
let reads = 0
const choices: Record<string, boolean> = {}
for (let index = 0; index < count; index++) {
Object.defineProperty(choices, `custom-${index}`, {
enumerable: true,
get: () => {
reads++
return true
}
})
}
choices.claude = false
renderSection({ agentDefaultSurface: 'terminal', nativeChatProviders: choices })
reads = 0
fireEvent.click(screen.getByRole('radio', { name: /^Chat/ }))
await waitFor(() => expect(toast.success).toHaveBeenCalledTimes(1))
expect(reads).toBe(count)
expect(settingsSet).toHaveBeenCalledTimes(1)
reads = 0
const action = vi.mocked(toast.success).mock.calls[0][1]?.action
if (!action || typeof action !== 'object' || !('onClick' in action)) {
throw new Error('Missing reset action')
}
fireEvent.click(
render(<button onClick={action.onClick}>Reset choices</button>).getByRole('button', {
name: 'Reset choices'
})
)
await waitFor(() => expect(settingsSet).toHaveBeenCalledTimes(2))
expect(reads).toBe(count)
expect(persisted.nativeChatProviders).not.toHaveProperty('claude')
expect(Object.keys(persisted.nativeChatProviders ?? {})).toHaveLength(count)
}
)
})