test: consolidate image-send coverage and repair contract assertions (#33093)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-24 01:36:55 -07:00
committed by GitHub
co-authored by OrcaWin
parent e496ea6f23
commit 9096344b8a
11 changed files with 114 additions and 39 deletions
@@ -45,7 +45,7 @@ describe('runInternalDevSetup', () => {
expect(
runInternalDevSetup({
env: {
ORCA_INTERNAL_DEV_SETUP: '/tmp/setup',
ORCA_INTERNAL_DEV_SETUP: ' /tmp/setup ',
ORCA_WORKTREE_PATH: '/tmp/worktree'
},
platform: 'linux',
@@ -59,14 +59,17 @@ describe('runInternalDevSetup', () => {
})
})
it('uses cmd.exe with explicit quoting so .cmd setup shims receive paths with spaces', () => {
it.each([
['C:\\repo with spaces\\worktree', 'C:\\repo with spaces\\worktree'],
['/c/repo with spaces/worktree', 'C:\\repo with spaces\\worktree']
])('quotes Windows setup paths and converts MSYS worktree %s', (worktree, expected) => {
const spawn = vi.fn(() => ({ status: 0 }))
expect(
runInternalDevSetup({
env: {
ORCA_INTERNAL_DEV_SETUP: 'C:\\tools\\setup.cmd',
ORCA_WORKTREE_PATH: 'C:\\repo\\worktree'
ORCA_INTERNAL_DEV_SETUP: 'C:\\tool suite\\setup.cmd',
ORCA_WORKTREE_PATH: worktree
},
platform: 'win32',
exists: () => true,
@@ -75,7 +78,7 @@ describe('runInternalDevSetup', () => {
).toBe(0)
expect(spawn).toHaveBeenCalledWith(
'cmd.exe',
['/d', '/s', '/c', 'call "C:\\tools\\setup.cmd" "C:\\repo\\worktree"'],
['/d', '/s', '/c', `call "C:\\tool suite\\setup.cmd" "${expected}"`],
{
stdio: 'inherit',
windowsVerbatimArguments: true
+13 -1
View File
@@ -3,6 +3,7 @@ import { tmpdir } from 'node:os'
import path from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { checkNativeBuildDirArtifacts, ensureSharedNativeBuild } from './shared-native-build.mjs'
import { peImage } from './windows-pe-image-fixture.mjs'
const roots = []
@@ -19,7 +20,7 @@ function makePackage() {
}
describe('ensureSharedNativeBuild', () => {
it('reuses a usable published entry without compiling, even when forced', () => {
it('reuses a usable published entry without compiling', () => {
const packageDir = makePackage()
const entryDir = path.join(packageDir, 'build', 'orca-napi-linux-x64')
mkdirSync(entryDir, { recursive: true })
@@ -90,6 +91,17 @@ describe('checkNativeBuildDirArtifacts', () => {
for (const file of ['conpty.node', 'conpty/conpty.dll', 'conpty/OpenConsole.exe']) {
writeFileSync(path.join(dir, file), 'not-a-pe')
}
writeFileSync(path.join(dir, 'conpty.node'), peImage({ arch: 'x64' }))
expect(
checkNativeBuildDirArtifacts('node-pty', dir, { platform: 'win32', arch: 'arm64' })
).toMatch(/not arm64/)
writeFileSync(path.join(dir, 'conpty.node'), peImage({ arch: 'arm64' }))
expect(
checkNativeBuildDirArtifacts('node-pty', dir, { platform: 'win32', arch: 'arm64' })
).toBe(null)
writeFileSync(path.join(dir, 'conpty.node'), 'not-a-pe')
expect(
checkNativeBuildDirArtifacts('node-pty', dir, { platform: 'win32', arch: 'arm64' })
).toMatch(/not arm64/)
@@ -29,6 +29,7 @@ function execute(snapshots) {
let now = 0
let index = 0
const calls = []
const delays = []
const github = {
rest: { actions: { listJobsForWorkflowRun: 'jobs' } },
async paginate(endpoint, input) {
@@ -38,12 +39,14 @@ function execute(snapshots) {
}
return {
calls,
delays,
result: new AsyncFunction('github', 'context', 'Date', 'setTimeout', step.with.script)(
github,
{ repo: { owner: 'owner', repo: 'repo' }, runId: 42 },
{ now: () => now },
(resolve) => {
now += 5000
(resolve, delay) => {
delays.push(delay)
now += delay
index++
resolve()
}
@@ -57,12 +60,19 @@ describe('early template assembly qualification gate', () => {
async (prefix) => {
const incomplete = lanes(prefix)
incomplete[1] = { ...incomplete[1], status: 'in_progress', conclusion: null }
const { calls, result } = execute([incomplete, lanes(prefix)])
const { calls, delays, result } = execute([incomplete, lanes(prefix)])
await result
expect(calls).toHaveLength(2)
expect(calls.every(({ input }) => input.filter === 'latest' && input.run_id === 42)).toBe(
true
)
expect(delays).toEqual([15_000])
for (const { endpoint, input } of calls) {
expect(endpoint).toBe('jobs')
expect(input).toMatchObject({
owner: 'owner',
repo: 'repo',
filter: 'latest',
run_id: 42
})
}
}
)
it.each(['failure', 'cancelled', 'skipped', 'timed_out', 'action_required', 'startup_failure'])(
@@ -41,6 +41,9 @@ describe('mobile native chat image preview reconciliation', () => {
deadline: null
}
expect(
findLandedImagePreviewEchoes(messages, [{ ...preview, baselineTailMessageId: 'prompt' }])
).toEqual([])
expect(findLandedUnconfirmedSends(messages, [unconfirmed])).toEqual([unconfirmed])
expect(findLandedImagePreviewEchoes(messages, [preview])).toEqual([
{ pendingId: 'pending', messageId: 'prompt', images: ['file:///a.jpg'] }
@@ -41,6 +41,7 @@ describe('uploadPickedNativeChatFiles', () => {
function run(files: AsyncIterable<PickedMobileFile>, failName?: string) {
const chips = new Map<string, PendingNativeChatFile>()
const errors: string[] = []
const pickedChips: PendingNativeChatFile[] = []
let id = 0
const done = uploadPickedNativeChatFiles({
picked: files,
@@ -51,7 +52,10 @@ describe('uploadPickedNativeChatFiles', () => {
return { path: `/store/${file.name}`, name: file.name, byteLength: 1 }
},
nextId: () => `file-${(id += 1)}`,
onPicked: (file) => chips.set(file.id, file),
onPicked: (file) => {
pickedChips.push(file)
chips.set(file.id, file)
},
onStored: (fileId, stored) => {
const chip = chips.get(fileId)
if (chip) {
@@ -61,12 +65,18 @@ describe('uploadPickedNativeChatFiles', () => {
onFailed: (fileId) => chips.delete(fileId),
onError: (message) => errors.push(message)
})
return { chips, errors, done }
return { chips, errors, pickedChips, done }
}
it('keeps every stored file as a chip with its host path', async () => {
const { chips, errors, done } = run(pickedFiles([picked('a.txt'), picked('b.txt')]))
const { chips, errors, pickedChips, done } = run(
pickedFiles([picked('a.txt'), picked('b.txt')])
)
await done
expect(pickedChips).toEqual([
{ id: 'file-1', name: 'a.txt', mime: 'text/plain', byteLength: 1, path: null },
{ id: 'file-2', name: 'b.txt', mime: 'text/plain', byteLength: 1, path: null }
])
expect([...chips.values()].map((chip) => chip.path)).toEqual(['/store/a.txt', '/store/b.txt'])
expect(errors).toEqual([])
})
@@ -59,7 +59,7 @@ describe('uploadMobileNativeChatImages', () => {
// Native chat defers the paste to submit — nothing is sent to the terminal here.
expect(client.calls.some((call) => call.method === 'terminal.send')).toBe(false)
const saveCall = client.calls.find((c) => c.method === 'clipboard.saveImageAsTempFile')
expect(saveCall?.params).toMatchObject({ connectionId: 'conn-7' })
expect(saveCall?.params).toEqual({ contentBase64: 'AAAA', connectionId: 'conn-7' })
})
it('uploads all three selected images in picker order', async () => {
@@ -114,7 +114,7 @@ describe('uploadMobileNativeChatImages', () => {
])
})
it('returns null when the picker is cancelled and uploads nothing', async () => {
it('returns an empty array when the picker is cancelled and uploads nothing', async () => {
const client = clientWithResponses([])
const result = await uploadMobileNativeChatImages('library', {
@@ -5,6 +5,8 @@ describe('isRenderableImageUri', () => {
it('accepts local previews and real URLs the device can load', () => {
for (const uri of [
'file:///var/mobile/a.jpg',
'FILE:///var/mobile/a.jpg',
'HTTPS://host/a.png',
'data:image/png;base64,AAAA',
'content://media/1',
'blob:abc',
@@ -190,21 +190,6 @@ describe('clearing a parked multi-line launch draft before the image paste', ()
expect(client.calls[0]?.params.text).toBe(clearInput)
})
it('falls back to a single Ctrl+U when no draft is parked', async () => {
const client = clientWithResponses([sendResult(true), sendResult(true)])
await pasteMobileNativeChatImagePaths({
client,
agent: 'claude',
terminal: 'term-1',
deviceToken: null,
imagePaths: ['/tmp/a.png'],
followedByText: true
})
expect(client.calls[0]?.params.text).toBe('\x15')
})
it('keeps image writes byte-clean when no text or submit follows', async () => {
const client = clientWithResponses([sendResult(true), sendResult(true), sendResult(true)])
@@ -36,22 +36,46 @@ describe('activateTabAndFocusPane', () => {
})
it('cancels a pending pane focus frame when a newer activation starts', () => {
const cancelAnimationFrame = vi.fn()
const frames = new Map<number, FrameRequestCallback>()
let frameId = 0
const cancelAnimationFrame = vi.fn((id: number) => frames.delete(id))
const dispatchEvent = vi.fn()
vi.stubGlobal(
'requestAnimationFrame',
vi.fn(() => 12)
vi.fn((callback: FrameRequestCallback) => {
frames.set(++frameId, callback)
return frameId
})
)
vi.stubGlobal('cancelAnimationFrame', cancelAnimationFrame)
vi.stubGlobal('window', {
dispatchEvent: vi.fn()
})
vi.stubGlobal('window', { dispatchEvent })
activateTabAndFocusPane('tab-1', 'leaf-1')
activateTabAndFocusPane('tab-2', 'leaf-2')
activateTabAndFocusPane('tab-2', 'leaf-2', {
ackPaneKeyOnSuccess: 'tab-2:leaf-2',
flashFocusedPane: true,
scrollToBottomIfOutputSinceLastView: true
})
expect(setActiveTab).toHaveBeenNthCalledWith(1, 'tab-1')
expect(setActiveTab).toHaveBeenNthCalledWith(2, 'tab-2')
expect(cancelAnimationFrame).toHaveBeenCalledWith(12)
expect(cancelAnimationFrame).toHaveBeenCalledWith(1)
expect(dispatchEvent).not.toHaveBeenCalled()
for (const callback of frames.values()) {
callback(0)
}
expect(dispatchEvent).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({
type: 'orca-focus-terminal-pane',
detail: {
tabId: 'tab-2',
leafId: 'leaf-2',
ackPaneKeyOnSuccess: 'tab-2:leaf-2',
flashFocusedPane: true,
scrollToBottomIfOutputSinceLastView: true
}
})
)
})
it('reveals the terminal surface before selecting the terminal tab', () => {
@@ -190,6 +190,17 @@ describe('active agent note send', () => {
'terminal.isRunningAgent',
'terminal.send'
])
expect(testState.callRuntimeRpc).toHaveBeenLastCalledWith(
{ kind: 'local' },
'terminal.send',
{
terminal: 'term-1',
text: 'File: src/app.ts',
enter: true,
client: { id: 'orca-desktop', type: 'desktop' }
},
{ timeoutMs: 15000 }
)
})
it('does not write notes when the active terminal is not an agent', async () => {
@@ -24,6 +24,7 @@ describe('agent session account home', () => {
isPersistedAgentSessionRecord({ ...record, accountHome: { variable, path: '/tmp/x' } })
expect(withVariable('CODEX_HOME')).toBe(true)
expect(withVariable('GROK_HOME')).toBe(true)
expect(withVariable('X'.repeat(128))).toBe(true)
for (const malformed of ['', 'A B', '1HOME', 'HOME=x', 'X'.repeat(129)]) {
expect(withVariable(malformed)).toBe(false)
}
@@ -40,6 +41,20 @@ describe('agent session account home', () => {
expect(isPersistedAgentSessionRecord({ ...record, accountHome: unmanaged })).toBe(true)
expect(agentSessionAccountHomesEqual(managed, unmanaged)).toBe(false)
expect(agentSessionAccountHomesEqual(managed, { ...managed })).toBe(true)
expect(
agentSessionAccountHomesEqual(managed, {
...managed,
locator: { ...managed.locator, managedProfileId: '223e4567-e89b-42d3-a456-426614174000' }
})
).toBe(false)
for (const managedProfileId of ['', 'not-a-profile-id']) {
expect(
isPersistedAgentSessionRecord({
...record,
accountHome: { ...managed, locator: { ...managed.locator, managedProfileId } }
})
).toBe(false)
}
expect(agentSessionAccountHomesEqual(unmanaged, { ...unmanaged })).toBe(true)
expect(
isPersistedAgentSessionRecord({