diff --git a/mobile/src/notifications/use-remote-push-capable-hosts.test.tsx b/mobile/src/notifications/use-remote-push-capable-hosts.test.tsx index 9b25ca3dcf9..b038b499ea2 100644 --- a/mobile/src/notifications/use-remote-push-capable-hosts.test.tsx +++ b/mobile/src/notifications/use-remote-push-capable-hosts.test.tsx @@ -180,17 +180,4 @@ describe('useRemotePushCapableHosts', () => { await answer('host-1', [CAPABILITY]) expect(latest).toEqual({ supported: true, resolved: true }) }) - - it('ignores an answer from a host the catalog no longer lists', async () => { - await mount() - await setClients([ - { hostId: 'host-ghost', client: clientFor('host-ghost'), state: 'connected' } - ]) - - await answer('host-ghost', [CAPABILITY]) - - // An unpaired desktop cannot push to this phone, so its vote must not offer - // the switch — nor count as the answer that resolves the section. - expect(latest).toEqual({ supported: false, resolved: false }) - }) }) diff --git a/mobile/src/onboarding/MobileOnboardingPage.test.ts b/mobile/src/onboarding/MobileOnboardingPage.test.ts index be539ea9208..fec0ef53169 100644 --- a/mobile/src/onboarding/MobileOnboardingPage.test.ts +++ b/mobile/src/onboarding/MobileOnboardingPage.test.ts @@ -97,7 +97,7 @@ describe('MobileOnboardingPage', () => { const callbacks = await renderPage('session-view') act(() => button('Open sessions in Chat UI').props.onPress()) - expect(callbacks.onSessionChoice).toHaveBeenCalledWith('chat') + expect(callbacks.onSessionChoice).toHaveBeenCalledExactlyOnceWith('chat') expect(callbacks.onNotificationChoice).not.toHaveBeenCalled() }) diff --git a/mobile/src/onboarding/NotificationOnboardingPreview.test.ts b/mobile/src/onboarding/NotificationOnboardingPreview.test.ts index c279ec60a2c..5016ba38bd6 100644 --- a/mobile/src/onboarding/NotificationOnboardingPreview.test.ts +++ b/mobile/src/onboarding/NotificationOnboardingPreview.test.ts @@ -87,6 +87,7 @@ describe('NotificationOnboardingPreview', () => { }) it('does not animate the banners when the page is off-screen', async () => { + mocks.reducedMotion = false await renderPreview(false) expect(mocks.loop).not.toHaveBeenCalled() }) diff --git a/mobile/src/settings/app-update-settings-rows.test.tsx b/mobile/src/settings/app-update-settings-rows.test.tsx index 77177bf7d63..b566cbc0508 100644 --- a/mobile/src/settings/app-update-settings-rows.test.tsx +++ b/mobile/src/settings/app-update-settings-rows.test.tsx @@ -65,7 +65,7 @@ describe('AppUpdateSettingsRows', () => { 'Last checked 2h ago' ]) act(() => pressables[0].props.onPress()) - expect(onUpdate).toHaveBeenCalledTimes(1) + expect(onUpdate).toHaveBeenCalledExactlyOnceWith('https://example.test') }) it('shows a plain, untappable version row when current, and Never before a first success', () => { diff --git a/mobile/src/settings/settings-screen-state.test.tsx b/mobile/src/settings/settings-screen-state.test.tsx index ba790d50abc..a0ec8801f13 100644 --- a/mobile/src/settings/settings-screen-state.test.tsx +++ b/mobile/src/settings/settings-screen-state.test.tsx @@ -153,7 +153,7 @@ describe('shared settings screen state', () => { // Without the request-epoch fence the stale read would flip the switch back on. expect(switchProps().value).toBe(false) }) - it('does not enable notifications after denied OS permission', async () => { + it('does not enable notifications when permission is not granted', async () => { const denied = { granted: false, status: 'undetermined', diff --git a/mobile/src/settings/voice-settings-poller-refresh.test.tsx b/mobile/src/settings/voice-settings-poller-refresh.test.tsx index ec3b4370e19..6a44f4f4415 100644 --- a/mobile/src/settings/voice-settings-poller-refresh.test.tsx +++ b/mobile/src/settings/voice-settings-poller-refresh.test.tsx @@ -6,7 +6,7 @@ import type { VoiceSettingsOperations } from './voice-settings-operations' // Why: the sibling state test stubs the poller, so it can only prove the screen's local // machine. This one runs the real useDictationSetupPoller, whose refreshNow is gated on -// visible && foreground, to prove the rejected-save recovery actually reaches the wire. +// visible && foreground, to prove rejected-save recovery reaches the load operation. vi.mock('react-native', () => ({ View: 'View', Text: 'Text', diff --git a/src/main/persistence/applying-settings/persistence-native-chat-tab-view-mode.test.ts b/src/main/persistence/applying-settings/persistence-native-chat-tab-view-mode.test.ts index 5de84f4dac3..4c05c32eb82 100644 --- a/src/main/persistence/applying-settings/persistence-native-chat-tab-view-mode.test.ts +++ b/src/main/persistence/applying-settings/persistence-native-chat-tab-view-mode.test.ts @@ -60,7 +60,7 @@ describe('Store native-chat tab viewMode persistence', () => { }) // Preserve absent viewMode for legacy tabs; renderer hydration supplies terminal mode. - it('round-trips viewMode for unified tabs and defaults legacy tabs to terminal', async () => { + it('loads chat viewMode and preserves missing legacy viewMode', async () => { const WORKTREE = 'repo1::/worktree' writeDataFile({ schemaVersion: 1, diff --git a/src/main/persistence/applying-settings/persistence-right-sidebar-tab.test.ts b/src/main/persistence/applying-settings/persistence-right-sidebar-tab.test.ts index 5b04ff67e3e..4cb794b84d6 100644 --- a/src/main/persistence/applying-settings/persistence-right-sidebar-tab.test.ts +++ b/src/main/persistence/applying-settings/persistence-right-sidebar-tab.test.ts @@ -22,7 +22,7 @@ describe('normalizeRightSidebarTab', () => { } ) - it('preserves the folder-only pr-checks tab across restarts', () => { + it('preserves the folder-only pr-checks tab', () => { expect(normalizeRightSidebarTab('pr-checks')).toBe('pr-checks') }) diff --git a/src/main/persistence/applying-settings/persistence-settings-update.test.ts b/src/main/persistence/applying-settings/persistence-settings-update.test.ts index 8800870d033..618095236b3 100644 --- a/src/main/persistence/applying-settings/persistence-settings-update.test.ts +++ b/src/main/persistence/applying-settings/persistence-settings-update.test.ts @@ -98,7 +98,7 @@ describe('Store', () => { expect(updated.branchPrefix).toBe('git-username') }) - it('persists the agent skill sharing capability as an exact boolean', async () => { + it('normalizes the agent skill sharing capability to an exact boolean', async () => { const store = await createStore() expect(store.updateSettings({ agentSkillSharingEnabled: true }).agentSkillSharingEnabled).toBe( diff --git a/src/main/persistence/loading-store/legacy-opencode-go-api-key-migration.test.ts b/src/main/persistence/loading-store/legacy-opencode-go-api-key-migration.test.ts index a5e94937b51..019330f25a1 100644 --- a/src/main/persistence/loading-store/legacy-opencode-go-api-key-migration.test.ts +++ b/src/main/persistence/loading-store/legacy-opencode-go-api-key-migration.test.ts @@ -119,6 +119,7 @@ describe('migrateLegacyOpenCodeGoApiKey', () => { expect(migrateLegacyOpenCodeGoApiKey(secrets, store)).toBe(true) expect(store.save).not.toHaveBeenCalled() expect(store.value()).toBe('fake-current-key') + expect(secrets.sealedBlob(LEGACY_OPENCODE_GO_API_KEY_SLOT)).toBeNull() expect(secretStore.decryptString).not.toHaveBeenCalled() expect(console.warn).toHaveBeenCalledWith( '[opencode-go] Kept the existing saved API key and dropped the one from settings.' diff --git a/src/main/persistence/loading-store/normalize-loaded-project-catalog.test.ts b/src/main/persistence/loading-store/normalize-loaded-project-catalog.test.ts index 8a535b41919..597a1bf6242 100644 --- a/src/main/persistence/loading-store/normalize-loaded-project-catalog.test.ts +++ b/src/main/persistence/loading-store/normalize-loaded-project-catalog.test.ts @@ -63,5 +63,13 @@ describe('normalizeLoadedProjectCatalog', () => { const result = normalizeLoadedProjectCatalog({}, vi.fn()) expect(result.projects).toEqual([]) expect(result.projectHostSetups).toEqual([]) + const malformed = corrupt(makeParsed([], []), { + projects: 'not-an-array', + projectHostSetups: {} + }) + expect(normalizeLoadedProjectCatalog(malformed, vi.fn())).toEqual({ + projects: [], + projectHostSetups: [] + }) }) }) diff --git a/src/renderer/src/components/tab-bar/TabBarCreateEntry.keyboard.test.tsx b/src/renderer/src/components/tab-bar/TabBarCreateEntry.keyboard.test.tsx index 035cba0c0c1..547462c9ca8 100644 --- a/src/renderer/src/components/tab-bar/TabBarCreateEntry.keyboard.test.tsx +++ b/src/renderer/src/components/tab-bar/TabBarCreateEntry.keyboard.test.tsx @@ -8,9 +8,6 @@ import type { TerminalTab } from '../../../../shared/terminal-tab-types' import type { Worktree } from '../../../../shared/worktree/types' import type { TabEntryOption } from './tab-create-entry-action' import type { TabAgentLaunchOption } from './tab-agent-launch-options' -// Unmocked on purpose: the empty-query message must stay in step with the -// placeholder rendered below. -import { getTabEntryOptions as classifyTabEntryOptions } from './tab-create-entry-classifier' import { TooltipProvider } from '@/components/ui/tooltip' import { useAppStore } from '@/store' import type { AppState } from '@/store/types' @@ -310,20 +307,6 @@ describe('TabBarCreateEntry keyboard navigation', () => { expect(input.getAttribute('aria-label')).toBe(placeholder) }) - it('states the same thing in the empty-query message as in the placeholder', () => { - mount() - - const [emptyOption] = classifyTabEntryOptions('', { - files: [], - loading: false, - loadError: null - }) - expect(emptyOption.classification).toMatchObject({ kind: 'empty' }) - expect( - emptyOption.classification.kind === 'empty' ? emptyOption.classification.message : null - ).toBe(container.querySelector('input')!.getAttribute('placeholder')) - }) - it('offers no switch rows when the menu opens with tabs open and no query', () => { seedOpenTabs() mount( diff --git a/src/renderer/src/components/tab-bar/TabStripScrollIndicator.test.tsx b/src/renderer/src/components/tab-bar/TabStripScrollIndicator.test.tsx index d7c64f2f518..8ac01b3eb5b 100644 --- a/src/renderer/src/components/tab-bar/TabStripScrollIndicator.test.tsx +++ b/src/renderer/src/components/tab-bar/TabStripScrollIndicator.test.tsx @@ -31,13 +31,6 @@ describe('TabStripScrollIndicator', () => { expect(indicator.className).toContain('opacity-0') }) - it('applies pointer-events-none when disabled', () => { - const { getByTestId } = render() - const indicator = getByTestId('tab-strip-scroll-indicator') - expect(indicator.className).toContain('pointer-events-none') - expect(indicator.className).not.toContain('group-hover/tab-strip:pointer-events-auto') - }) - it('stays hidden and unexpanded on hover when disabled', () => { const { getByTestId } = render() const indicator = getByTestId('tab-strip-scroll-indicator') @@ -113,6 +106,7 @@ describe('TabStripScrollIndicator', () => { expect(scrollToMock).toHaveBeenCalledTimes(1) expect(scrollToMock).toHaveBeenCalledWith( expect.objectContaining({ + left: 300, behavior: 'smooth' }) ) diff --git a/src/shared/feedback-image-limits.test.ts b/src/shared/feedback-image-limits.test.ts index 171364886de..3d0f80093a0 100644 --- a/src/shared/feedback-image-limits.test.ts +++ b/src/shared/feedback-image-limits.test.ts @@ -1,9 +1,5 @@ import { describe, expect, it } from 'vitest' -import { - MAX_FEEDBACK_IMAGE_BYTES, - MAX_FEEDBACK_IMAGE_COUNT, - MAX_FEEDBACK_IMAGE_TOTAL_BYTES -} from './feedback-image-limits' +import { MAX_FEEDBACK_IMAGE_BYTES, MAX_FEEDBACK_IMAGE_TOTAL_BYTES } from './feedback-image-limits' // Why: the exact value is a judgement call, staying under the host's limit is // not. https://www.onorca.dev/v1/feedback is a Vercel Function and rejects @@ -19,8 +15,4 @@ describe('feedback image limits', () => { it('cannot let one image exceed the whole budget', () => { expect(MAX_FEEDBACK_IMAGE_BYTES).toBeLessThanOrEqual(MAX_FEEDBACK_IMAGE_TOTAL_BYTES) }) - - it('leaves room for more than one attachment', () => { - expect(MAX_FEEDBACK_IMAGE_COUNT).toBeGreaterThan(1) - }) })