mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 10:27:33 +00:00
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:
@@ -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))
|
||||
})
|
||||
-14
@@ -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
-90
@@ -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 }) }
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
-39
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user