mirror of
https://github.com/stablyai/orca.git
synced 2026-10-16 16:03:26 +00:00
feat: computer use via agent-browser CDP bridge (#856)
This commit is contained in:
@@ -764,4 +764,158 @@ describe('OrcaRuntimeService', () => {
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
describe('browser page targeting', () => {
|
||||
it('passes explicit page ids through without resolving the current worktree', async () => {
|
||||
vi.mocked(listWorktrees).mockClear()
|
||||
const runtime = createRuntime()
|
||||
const snapshotMock = vi.fn().mockResolvedValue({
|
||||
browserPageId: 'page-1',
|
||||
snapshot: 'tree',
|
||||
refs: [],
|
||||
url: 'https://example.com',
|
||||
title: 'Example'
|
||||
})
|
||||
|
||||
runtime.setAgentBrowserBridge({
|
||||
snapshot: snapshotMock
|
||||
} as never)
|
||||
|
||||
const result = await runtime.browserSnapshot({ page: 'page-1' })
|
||||
|
||||
expect(result.browserPageId).toBe('page-1')
|
||||
expect(snapshotMock).toHaveBeenCalledWith(undefined, 'page-1')
|
||||
expect(listWorktrees).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('resolves explicit worktree selectors when page ids are also provided', async () => {
|
||||
vi.mocked(listWorktrees).mockClear()
|
||||
const runtime = createRuntime()
|
||||
const snapshotMock = vi.fn().mockResolvedValue({
|
||||
browserPageId: 'page-1',
|
||||
snapshot: 'tree',
|
||||
refs: [],
|
||||
url: 'https://example.com',
|
||||
title: 'Example'
|
||||
})
|
||||
|
||||
runtime.setAgentBrowserBridge({
|
||||
snapshot: snapshotMock,
|
||||
getRegisteredTabs: vi.fn(() => new Map([['page-1', 1]]))
|
||||
} as never)
|
||||
|
||||
await runtime.browserSnapshot({
|
||||
worktree: 'branch:feature/foo',
|
||||
page: 'page-1'
|
||||
})
|
||||
|
||||
expect(snapshotMock).toHaveBeenCalledWith(TEST_WORKTREE_ID, 'page-1')
|
||||
})
|
||||
|
||||
it('routes tab switch and capture start by explicit page id', async () => {
|
||||
const runtime = createRuntime()
|
||||
const tabSwitchMock = vi.fn().mockResolvedValue({
|
||||
switched: 2,
|
||||
browserPageId: 'page-2'
|
||||
})
|
||||
const captureStartMock = vi.fn().mockResolvedValue({
|
||||
capturing: true
|
||||
})
|
||||
|
||||
runtime.setAgentBrowserBridge({
|
||||
tabSwitch: tabSwitchMock,
|
||||
captureStart: captureStartMock
|
||||
} as never)
|
||||
|
||||
await expect(runtime.browserTabSwitch({ page: 'page-2' })).resolves.toEqual({
|
||||
switched: 2,
|
||||
browserPageId: 'page-2'
|
||||
})
|
||||
await expect(runtime.browserCaptureStart({ page: 'page-2' })).resolves.toEqual({
|
||||
capturing: true
|
||||
})
|
||||
expect(tabSwitchMock).toHaveBeenCalledWith(undefined, undefined, 'page-2')
|
||||
expect(captureStartMock).toHaveBeenCalledWith(undefined, 'page-2')
|
||||
})
|
||||
|
||||
it('does not silently drop invalid explicit worktree selectors for page-targeted commands', async () => {
|
||||
vi.mocked(listWorktrees).mockResolvedValue(MOCK_GIT_WORKTREES)
|
||||
const runtime = createRuntime()
|
||||
const snapshotMock = vi.fn()
|
||||
|
||||
runtime.setAgentBrowserBridge({
|
||||
snapshot: snapshotMock,
|
||||
getRegisteredTabs: vi.fn(() => new Map([['page-1', 1]]))
|
||||
} as never)
|
||||
|
||||
await expect(
|
||||
runtime.browserSnapshot({
|
||||
worktree: 'path:/tmp/missing-worktree',
|
||||
page: 'page-1'
|
||||
})
|
||||
).rejects.toThrow('selector_not_found')
|
||||
expect(snapshotMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not silently drop invalid explicit worktree selectors for non-page browser commands', async () => {
|
||||
vi.mocked(listWorktrees).mockResolvedValue(MOCK_GIT_WORKTREES)
|
||||
const runtime = createRuntime()
|
||||
const tabListMock = vi.fn()
|
||||
|
||||
runtime.setAgentBrowserBridge({
|
||||
tabList: tabListMock
|
||||
} as never)
|
||||
|
||||
await expect(
|
||||
runtime.browserTabList({
|
||||
worktree: 'path:/tmp/missing-worktree'
|
||||
})
|
||||
).rejects.toThrow('selector_not_found')
|
||||
expect(tabListMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('rejects closing an unknown page id instead of treating it as success', async () => {
|
||||
vi.mocked(listWorktrees).mockResolvedValue(MOCK_GIT_WORKTREES)
|
||||
const runtime = createRuntime()
|
||||
|
||||
runtime.setAgentBrowserBridge({
|
||||
getRegisteredTabs: vi.fn(() => new Map([['page-1', 1]]))
|
||||
} as never)
|
||||
|
||||
await expect(
|
||||
runtime.browserTabClose({
|
||||
page: 'missing-page'
|
||||
})
|
||||
).rejects.toThrow('Browser page missing-page was not found')
|
||||
})
|
||||
|
||||
it('rejects closing a page outside the explicitly scoped worktree', async () => {
|
||||
vi.mocked(listWorktrees).mockResolvedValue([
|
||||
...MOCK_GIT_WORKTREES,
|
||||
{
|
||||
path: '/tmp/worktree-b',
|
||||
head: 'def',
|
||||
branch: 'feature/bar',
|
||||
isBare: false,
|
||||
isMainWorktree: false
|
||||
}
|
||||
])
|
||||
const runtime = createRuntime()
|
||||
const getRegisteredTabsMock = vi.fn((worktreeId?: string) =>
|
||||
worktreeId === `${TEST_REPO_ID}::/tmp/worktree-b` ? new Map() : new Map([['page-1', 1]])
|
||||
)
|
||||
|
||||
runtime.setAgentBrowserBridge({
|
||||
getRegisteredTabs: getRegisteredTabsMock
|
||||
} as never)
|
||||
|
||||
await expect(
|
||||
runtime.browserTabClose({
|
||||
page: 'page-1',
|
||||
worktree: 'path:/tmp/worktree-b'
|
||||
})
|
||||
).rejects.toThrow('Browser page page-1 was not found in this worktree')
|
||||
expect(getRegisteredTabsMock).toHaveBeenCalledWith(`${TEST_REPO_ID}::/tmp/worktree-b`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user