test: remove low-value mobile suites and redundant assertions (#42390)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-11-14 05:28:55 -08:00
committed by GitHub
co-authored by OrcaWin
parent c445f7dbc1
commit 3c3ffdc155
6 changed files with 2 additions and 381 deletions
@@ -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<typeof MobileNativeChatAsk>[0]['onAnswer']
function mount(value: AskPrompt): Mock<Answer> {
const onAnswer = vi.fn<Answer>(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))
})
@@ -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<T>() {
let resolve!: (value: T) => void
const promise = new Promise<T>((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,
@@ -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<StructuredAgentSessionMutationResult<AgentSessionOptionResult>>()
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<boolean> | 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<AgentSessionModelCatalogResult>()
let reads = 0
const client = optionsClient(
() =>
++reads === 1
? Promise.resolve({
...OPTIONS,
current: {
model: 'gpt-live',
effort: 'high',
defaulted: ['model'],
acceptedInputSources: { effort: 'saved' }
}
})
: deferred<AgentSessionOptionsResult>().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 () => ({
@@ -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([])
})
})
@@ -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 }) }
)
})
})
@@ -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<HTMLInputElement>('input'),
document.body.querySelector<HTMLTextAreaElement>('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'