From 3c3ffdc155dc75a2348ddc49b8b4fc2cbb78bb19 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Sat, 14 Nov 2026 05:28:55 -0800 Subject: [PATCH] test: remove low-value mobile suites and redundant assertions (#42390) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../MobileNativeChatAsk.previews.test.tsx | 120 ------------------ ...e-structured-agent-options.test-fixture.ts | 14 -- ...se-mobile-structured-agent-options.test.ts | 91 +------------ .../MobileNativeChatLiveLine.android.test.ts | 100 --------------- .../linear-issue-project-selector.test.tsx | 19 +-- .../TerminalQuickCommandDialog.test.tsx | 39 ------ 6 files changed, 2 insertions(+), 381 deletions(-) delete mode 100644 mobile/src/session/native-chat/input/prompts/MobileNativeChatAsk.previews.test.tsx delete mode 100644 mobile/src/session/native-chat/transcript/MobileNativeChatLiveLine.android.test.ts diff --git a/mobile/src/session/native-chat/input/prompts/MobileNativeChatAsk.previews.test.tsx b/mobile/src/session/native-chat/input/prompts/MobileNativeChatAsk.previews.test.tsx deleted file mode 100644 index 68cdc244089..00000000000 --- a/mobile/src/session/native-chat/input/prompts/MobileNativeChatAsk.previews.test.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { createElement, type ReactNode } from 'react' -import { act, create, type ReactTestInstance, type ReactTestRenderer } from 'react-test-renderer' -import { afterEach, expect, it, vi, type Mock } from 'vitest' -import type { AskPrompt } from '../../../../../../src/shared/native-chat/input/questions/native-chat-ask-types' -import { MobileNativeChatAsk } from './MobileNativeChatAsk' - -vi.mock('react-native', () => ({ - Keyboard: { dismiss: vi.fn() }, - Platform: { select: (options: { ios: unknown }) => options.ios }, - Pressable: 'Pressable', - ScrollView: 'ScrollView', - Text: 'Text', - View: 'View', - StyleSheet: { create: (styles: unknown) => styles, hairlineWidth: 1 } -})) -vi.mock('lucide-react-native', () => ({ Check: 'Check' })) -vi.mock('../../../../components/composer-input/MessageBox', () => ({ MessageBox: 'MessageBox' })) -vi.mock('../../transcript/MobileNativeChatQuestionText', () => ({ - MobileNativeChatQuestionText: 'QuestionText' -})) -vi.mock('./MobileNativeChatCardHeaderAction', () => ({ - MobileNativeChatCardHeaderAction: () => null -})) -vi.mock('../../../../components/bottom-drawer/keyed-bottom-drawer', () => ({ - KeyedBottomDrawer: ({ - sheet, - children - }: { - sheet: number | null - children: (index: number) => ReactNode - }) => (sheet === null ? null : children(sheet)) -})) -vi.mock('../../../../components/nav-bar/SheetHeader', () => ({ - SheetHeader: ({ onClose }: { onClose: () => void }) => - createElement('button', { accessibilityLabel: 'Close viewer', onPress: onClose }) -})) - -let renderer: ReactTestRenderer -function prompt(multiSelect = false): AskPrompt { - return { - questions: [ - { - question: 'Which layout?', - header: 'Layout', - multiSelect, - options: [ - { label: 'First', preview: 'A\t|\n𠀀\t|' }, - { label: 'No preview' }, - { label: 'Last', preview: 'Last preview body' } - ] - } - ] - } -} -function buttons(): ReactTestInstance[] { - return renderer.root.findAll( - (node) => String(node.type) === 'Pressable' || String(node.type) === 'button' - ) -} -function button(label: string): ReactTestInstance { - return buttons().find((node) => node.props.accessibilityLabel === label)! -} -function row(index: number): ReactTestInstance { - return buttons().filter((node) => ['radio', 'checkbox'].includes(node.props.accessibilityRole))[ - index - ]! -} -function textAction(label: string): ReactTestInstance { - return buttons().find((node) => - node - .findAll((child) => String(child.type) === 'Text') - .some((text) => text.children.includes(label)) - )! -} -function press(node: ReactTestInstance): void { - act(() => node.props.onPress()) -} -type Answer = Parameters[0]['onAnswer'] -function mount(value: AskPrompt): Mock { - const onAnswer = vi.fn(async () => true) - act(() => { - renderer = create(createElement(MobileNativeChatAsk, { prompt: value, onAnswer })) - }) - return onAnswer -} - -afterEach(() => { - act(() => renderer?.unmount()) - vi.useRealTimers() -}) - -it('inspects every preview without choosing, and keeps the original option when choosing and submitting', async () => { - vi.useFakeTimers() - const onAnswer = mount(prompt()) - expect(row(2).props.accessibilityState.checked).toBe(false) - press(button('First preview')) - // Paging crosses the option with no preview; inspection does not change the answer. - press( - buttons().find( - (node) => node.props.accessibilityLabel === 'Last preview' && node.props.accessibilityState - )! - ) - press(button('Close viewer')) - act(() => { - vi.runAllTimers() - }) - expect(row(2).props.accessibilityState.checked).toBe(false) - press(button('Last preview')) - press(button('Choose this')) - act(() => { - vi.runAllTimers() - }) - expect(row(2).props.accessibilityState.checked).toBe(true) - press(button('Last preview')) - press(button('Chosen')) - press(row(2)) - expect(row(2).props.accessibilityState.checked).toBe(true) - await act(async () => textAction('Submit').props.onPress()) - expect(onAnswer).toHaveBeenCalledWith([{ indices: [2] }], expect.any(Function)) -}) diff --git a/mobile/src/session/native-chat/input/session-options/use-mobile-structured-agent-options.test-fixture.ts b/mobile/src/session/native-chat/input/session-options/use-mobile-structured-agent-options.test-fixture.ts index d22f225958a..a1196a4355a 100644 --- a/mobile/src/session/native-chat/input/session-options/use-mobile-structured-agent-options.test-fixture.ts +++ b/mobile/src/session/native-chat/input/session-options/use-mobile-structured-agent-options.test-fixture.ts @@ -41,18 +41,6 @@ const OPTIONS: AgentSessionOptionsResult = { current: { model: 'gpt-live', effort: 'medium' } } -const FAST_OPTIONS: AgentSessionOptionsResult = { - ...OPTIONS, - models: OPTIONS.models.map((model) => ({ ...model, supportsFastMode: true })), - fastModeSupport: { supported: true }, - current: { ...OPTIONS.current, fastMode: false, confirmed: ['fastMode'] } -} - -const FAST_OPTIONS_ON: AgentSessionOptionsResult = { - ...FAST_OPTIONS, - current: { ...FAST_OPTIONS.current, fastMode: true } -} - function deferred() { let resolve!: (value: T) => void const promise = new Promise((accept) => { @@ -235,8 +223,6 @@ const BASE = { agent: 'codex', sessionId: 'session-1', fence: 1 } as const export { settle, OPTIONS, - FAST_OPTIONS, - FAST_OPTIONS_ON, deferred, rpcSuccess, optionsClient, diff --git a/mobile/src/session/native-chat/input/session-options/use-mobile-structured-agent-options.test.ts b/mobile/src/session/native-chat/input/session-options/use-mobile-structured-agent-options.test.ts index de85158f8d1..1e80b079dc8 100644 --- a/mobile/src/session/native-chat/input/session-options/use-mobile-structured-agent-options.test.ts +++ b/mobile/src/session/native-chat/input/session-options/use-mobile-structured-agent-options.test.ts @@ -1,22 +1,15 @@ import { structuredAgentSessionOptionStamp } from '../../../../../../src/shared/agent-session/options/reconciliation/structured-agent-session-option-state' import { act } from 'react-test-renderer' import { describe, expect, it, vi } from 'vitest' -import type { - AgentSessionModelCatalogResult, - AgentSessionOptionResult -} from '../../../../../../src/shared/agent-session/agent-session-wire' +import type { AgentSessionModelCatalogResult } from '../../../../../../src/shared/agent-session/agent-session-wire' import type { AgentSessionOptionsResult } from '../../../../../../src/shared/agent-session/options/protocol/agent-session-options-result' -import type { StructuredAgentSessionMutationResult } from '../../agent-session/mobile-structured-agent-session-rpc' import { rememberMobileCreatedStructuredSession } from '../../agent-session/mobile-created-structured-sessions' import { settle, OPTIONS, - FAST_OPTIONS, - FAST_OPTIONS_ON, deferred, optionsClient, - queuedReads, recordingMutate, accepted, mountOptions, @@ -25,44 +18,6 @@ import { BASE } from './use-mobile-structured-agent-options.test-fixture' -describe('useMobileStructuredAgentOptions pending picks', () => { - it('holds a repeat of an option on its way but lets another option go, as desktop does', async () => { - const client = optionsClient(queuedReads(FAST_OPTIONS, FAST_OPTIONS_ON)) - const inFlight = deferred>() - const { mutate } = recordingMutate(async (call) => - call === 0 ? inFlight.promise : accepted({ key: 'fastMode', value: 'true' }, false) - ) - const harness = await mountOptions({ ...BASE, client: client.client, mutate }) - - let firstWrite: Promise | null = null - await act(async () => { - firstWrite = harness.current().setStructuredOption('effort', 'high') - }) - expect(harness.current().pendingOptionId).toBe('effort') - - let repeat: boolean | null = null - await act(async () => { - repeat = await harness.current().setStructuredOption('effort', 'medium') - }) - expect(repeat).toBe(false) - - let other: boolean | null = null - await act(async () => { - other = await harness.current().setStructuredOption('fastMode', true) - }) - expect(other).toBe(true) - - await act(async () => { - inFlight.resolve(accepted({ key: 'effort', value: 'high' }, false)) - await firstWrite - }) - await settle() - expect(await firstWrite).toBe(true) - expect(harness.current().pendingOptionId).toBeNull() - await harness.unmount() - }) -}) - describe('useMobileStructuredAgentOptions host catalog', () => { const HOST_CATALOG: AgentSessionModelCatalogResult = { origin: 'probe', @@ -210,50 +165,6 @@ it('joins the post-receipt menu read with an unchanged phone option stamp', asyn await harness.unmount() }) -it('phone retains a queried seed-catalog base across its fence until later metadata arrives', async () => { - const catalog = deferred() - let reads = 0 - const client = optionsClient( - () => - ++reads === 1 - ? Promise.resolve({ - ...OPTIONS, - current: { - model: 'gpt-live', - effort: 'high', - defaulted: ['model'], - acceptedInputSources: { effort: 'saved' } - } - }) - : deferred().promise, - () => catalog.promise - ) - const { mutate } = recordingMutate(async () => accepted({ key: 'effort', value: 'low' }, true)) - rememberMobileCreatedStructuredSession('answered-retention', 'worktree') - const harness = await mountOptions({ - ...BASE, - sessionId: 'answered-retention', - client: client.client, - mutate, - sendSelection: true, - coherentCatalog: true - }) - expect(harness.current().sendSelection()).toEqual({ selection: { effort: 'high' } }) - await harness.rerender({ fence: 2 }) - expect(harness.current().sendSelection()).toEqual({ selection: { effort: 'high' } }) - await act(async () => - catalog.resolve({ - origin: 'probe', - fetchedAt: 1, - models: OPTIONS.models, - listingNamesConfiguredModel: true - }) - ) - await settle() - expect(harness.current().sendSelection()).toEqual({ selection: { effort: 'high' } }) - await harness.unmount() -}) - it('refreshes saved planning intent from the same host status stamp without remounting', async () => { let interactionMode: 'default' | 'plan' = 'default' const client = optionsClient(async () => ({ diff --git a/mobile/src/session/native-chat/transcript/MobileNativeChatLiveLine.android.test.ts b/mobile/src/session/native-chat/transcript/MobileNativeChatLiveLine.android.test.ts deleted file mode 100644 index 927f4c7ea14..00000000000 --- a/mobile/src/session/native-chat/transcript/MobileNativeChatLiveLine.android.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { createElement, type ReactNode } from 'react' -import { act, create, type ReactTestInstance, type ReactTestRenderer } from 'react-test-renderer' -import { afterEach, describe, expect, it, vi } from 'vitest' -import type { NativeChatLiveReasoning } from '../../../../../src/shared/native-chat/transcript/row-content/native-chat-reasoning-row' - -// Android lacks inline selection; exercise the message-actions copy path separately from iOS. -vi.mock('react-native', async () => { - const React = await import('react') - const host = - (name: string) => - ({ children, ...props }: { children?: ReactNode }): ReactNode => - React.createElement(name, props, children) - return { - ActivityIndicator: host('ActivityIndicator'), - Platform: { OS: 'android' }, - Pressable: host('Pressable'), - Text: host('Text'), - View: host('View'), - StyleSheet: { create: (styles: unknown) => styles, hairlineWidth: 1 } - } -}) -vi.mock('lucide-react-native', () => ({ ChevronRight: 'ChevronRight' })) -vi.mock('../../../components/LoadingSpinner', () => ({ LoadingSpinner: 'LoadingSpinner' })) -vi.mock('./MobileNativeChatReasoningRow', () => ({ - MobileNativeChatReasoningBody: 'ReasoningBody' -})) -vi.mock('./MobileNativeChatRewindDialog', () => ({ MobileNativeChatRewindDialog: () => null })) -vi.mock('./MobileNativeChatMessageActionsSheet', () => ({ - MobileNativeChatMessageActionsSheet: 'MessageActionsSheet' -})) -vi.mock('./MobileLinkActionSheet', () => ({ MobileLinkActionSheet: 'LinkActionSheet' })) -vi.mock('../visual/MobileNativeChatImageViewer', () => ({ - MobileNativeChatImageViewer: 'ImageViewer' -})) -vi.mock('../visual/MobileImageViewerActions', () => ({ - MobileImageViewerActions: 'ImageViewerActions' -})) - -import { MobileNativeChatLiveLine } from './MobileNativeChatLiveLine' -import { MobileNativeChatRowOverlaysHost } from './MobileNativeChatRowOverlaysHost' - -const block: NativeChatLiveReasoning = { - message: { - id: 'r-1', - role: 'reasoning', - blocks: [{ type: 'text', text: 'Weighing two approaches' }], - timestamp: null, - source: 'transcript', - state: 'running' - }, - markdown: 'Weighing two approaches' -} - -const chat = (reasoning: NativeChatLiveReasoning | null | undefined) => - createElement( - MobileNativeChatRowOverlaysHost, - { conversation: { surfaceId: 'chat', sessionId: 's1' } }, - reasoning === undefined - ? null - : createElement(MobileNativeChatLiveLine, { - line: { - thinking: true, - stopping: false, - activityText: null, - reasoning, - reasoningExpanded: true - }, - onToggleReasoning: vi.fn(), - fontScale: 1 - }) - ) - -describe('MobileNativeChatLiveLine on Android', () => { - let renderer: ReactTestRenderer | null = null - afterEach(() => { - act(() => renderer?.unmount()) - renderer = null - }) - - const byType = (type: string): ReactTestInstance[] => - renderer!.root.findAll((node) => String(node.type) === type) - const sheetIds = (): string[] => - byType('MessageActionsSheet').map((sheet) => sheet.props.message.id) - - it('keeps the sheet open when the turn finishes, and opens none for the next block by itself', () => { - act(() => { - renderer = create(chat(block)) - }) - act(() => byType('ReasoningBody')[0]!.props.onLongPress()) - act(() => renderer!.update(chat(undefined))) - expect(sheetIds()).toEqual(['r-1']) - - act(() => - renderer!.update(chat({ message: { ...block.message, id: 'r-2' }, markdown: 'Next thought' })) - ) - expect(sheetIds()).toEqual(['r-1']) - act(() => byType('MessageActionsSheet')[0]!.props.onClose()) - expect(sheetIds()).toEqual([]) - }) -}) diff --git a/src/renderer/src/components/linear-issue-project-selector.test.tsx b/src/renderer/src/components/linear-issue-project-selector.test.tsx index 5d83373bb7f..02bcd938877 100644 --- a/src/renderer/src/components/linear-issue-project-selector.test.tsx +++ b/src/renderer/src/components/linear-issue-project-selector.test.tsx @@ -83,7 +83,7 @@ afterEach(() => { }) describe('LinearIssueProjectSelector', () => { - it('ignores stale searches and preserves exact source-scoped assignment calls', async () => { + it('keeps current search results for source-scoped project selection', async () => { const first = deferred<{ items: LinearProjectSummary[] }>() const second = deferred<{ items: LinearProjectSummary[] }>() runtimeMocks.linearListProjects @@ -105,21 +105,9 @@ describe('LinearIssueProjectSelector', () => { const input = document.body.querySelector('input') as HTMLInputElement act(() => setInputValue(input, 'first')) await act(async () => vi.advanceTimersByTimeAsync(150)) - expect(runtimeMocks.linearListProjects).toHaveBeenLastCalledWith( - sourceContext, - 'first', - 20, - 'workspace-1' - ) act(() => setInputValue(input, 'second')) await act(async () => vi.advanceTimersByTimeAsync(150)) - expect(runtimeMocks.linearListProjects).toHaveBeenLastCalledWith( - sourceContext, - 'second', - 20, - 'workspace-1' - ) const currentProject = { id: 'project-2', name: 'Second project' } await act(async () => { second.resolve({ items: [currentProject] }) @@ -145,10 +133,5 @@ describe('LinearIssueProjectSelector', () => { { projectId: 'project-2' }, 'workspace-1' ) - expect(storeMocks.state.patchLinearIssue).toHaveBeenCalledWith( - 'issue-1', - { project: currentProject }, - { scope: expect.objectContaining({ settings: sourceContext }) } - ) }) }) diff --git a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialog.test.tsx b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialog.test.tsx index d343c0bde4d..2711a2e0c5f 100644 --- a/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialog.test.tsx +++ b/src/renderer/src/components/terminal-quick-commands/TerminalQuickCommandDialog.test.tsx @@ -56,43 +56,6 @@ describe('TerminalQuickCommandDialog animation structure', () => { vi.unstubAllGlobals() }) - it('selects all text in editable fields with Cmd+A', async () => { - await renderDialog({ - id: 'qc-select-all', - label: 'Start dev server', - action: 'terminal-command', - command: 'npm run dev', - appendEnter: true, - scope: { type: 'global' } - }) - - const fields = [ - document.body.querySelector('input'), - document.body.querySelector('textarea[aria-label="Command"]') - ] - - for (const field of fields) { - expect(field).not.toBeNull() - if (!field) { - continue - } - field.setSelectionRange(field.value.length, field.value.length) - await act(async () => { - field.dispatchEvent( - new KeyboardEvent('keydown', { - key: 'a', - code: 'KeyA', - metaKey: true, - bubbles: true, - cancelable: true - }) - ) - }) - - expect([field.selectionStart, field.selectionEnd]).toEqual([0, field.value.length]) - } - }) - it('refuses to save a prompt over an older host cap instead of truncating it', async () => { const onSave = vi.fn() await renderDialog( @@ -142,8 +105,6 @@ describe('TerminalQuickCommandDialog animation structure', () => { }, { onSave, onOpenChange } ) - expect(document.body.textContent).not.toContain('characters') - expect(document.body.textContent).toContain('Drag corner to resize') const textarea = document.body.querySelector('textarea')! const cancel = Array.from(document.body.querySelectorAll('button')).find( (button) => button.textContent === 'Cancel'