mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user