Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('filesystem_bash', () => {
})

it('times out long-running commands', async () => {
const result = await exec({ command: 'sleep 30', timeout: 1 })
const result = await exec({ command: 'exec sleep 30', timeout: 1 })
expect(result.isError).toBe(true)
expect(result.text).toContain('timed out')
}, 10_000)
Expand Down
73 changes: 43 additions & 30 deletions packages/browseros-agent/apps/server/tests/tools/input.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { describe, it } from 'bun:test'
import assert from 'node:assert'
import type { Browser } from '../../src/browser/browser'
import { executeTool, type ToolContext } from '../../src/tools/framework'
import {
check,
click,
Expand Down Expand Up @@ -320,37 +322,48 @@ describe('input tools', () => {
}, 60_000)

it('scroll dispatches without error', async () => {
await withBrowser(async ({ execute }) => {
const newResult = await execute(new_page, {
url: FORM_PAGE,
})
const pageId = pageIdOf(newResult)

const before = await execute(evaluate_script, {
page: pageId,
expression: 'window.scrollY',
})

const scrollResult = await execute(scroll, {
page: pageId,
direction: 'down',
amount: 5,
})
assert.ok(!scrollResult.isError, textOf(scrollResult))
assert.ok(textOf(scrollResult).includes('Scrolled down'))

const after = await execute(evaluate_script, {
page: pageId,
expression: 'window.scrollY',
})
assert.ok(
Number(textOf(after)) > Number(textOf(before)),
`Expected scrollY to increase, before=${textOf(before)} after=${textOf(after)}`,
)

await execute(close_page, { page: pageId })
const calls: Array<{
page: number
direction: string
amount: number
element?: number
}> = []
const browser = {
getTabIdForPage: () => undefined,
scroll: async (
page: number,
direction: string,
amount: number,
element?: number,
) => {
calls.push({ page, direction, amount, element })
},
} as unknown as Browser
const ctx: ToolContext = {
browser,
directories: { workingDir: process.cwd() },
}

const result = await executeTool(
scroll,
{ page: 7, direction: 'down', amount: 5 },
ctx,
AbortSignal.timeout(1_000),
)

assert.ok(!result.isError, textOf(result))
assert.ok(textOf(result).includes('Scrolled down'))
assert.deepStrictEqual(calls, [
{ page: 7, direction: 'down', amount: 5, element: undefined },
])
assert.deepStrictEqual(structuredOf(result), {
action: 'scroll',
page: 7,
direction: 'down',
amount: 5,
element: undefined,
})
}, 60_000)
})

it('hover moves cursor over element', async () => {
await withBrowser(async ({ execute }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ describe('observation tools', () => {
savedPath = data.path

assert.strictEqual(data.writtenToFile, true)
assert.ok(textOf(contentResult).includes('Saved page content'))
assert.ok(textOf(contentResult).includes('Content truncated'))
assert.ok(textOf(contentResult).includes(savedPath))
assert.ok(existsSync(savedPath), 'Saved page content file should exist')
assert.ok(
dirname(savedPath).startsWith(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ function structuredOf<T>(result: { structuredContent?: unknown }): T {

function createToolContext(
browser: Browser,
executionDir: string,
workingDir: string,
resourcesDir?: string,
): ToolContext {
return {
browser,
directories: {
executionDir,
workingDir,
resourcesDir,
},
}
Expand All @@ -50,10 +50,8 @@ function createBrowserStub(methods: Record<string, unknown>): Browser {
}

describe('page action tools', () => {
it('save_pdf resolves relative paths against the execution directory by default', async () => {
const executionDir = await mkdtemp(
join(tmpdir(), 'browseros-page-actions-'),
)
it('save_pdf resolves relative paths against the working directory by default', async () => {
const workingDir = await mkdtemp(join(tmpdir(), 'browseros-page-actions-'))
const browser = createBrowserStub({
printToPDF: async () => ({
data: Buffer.from('pdf-data').toString('base64'),
Expand All @@ -64,26 +62,24 @@ describe('page action tools', () => {
const result = await executeTool(
save_pdf,
{ page: 1, path: 'report.pdf' },
createToolContext(browser, executionDir),
createToolContext(browser, workingDir),
AbortSignal.timeout(1_000),
)

assert.ok(!result.isError, textOf(result))
const outputPath = join(executionDir, 'report.pdf')
const outputPath = join(workingDir, 'report.pdf')
assert.strictEqual(
structuredOf<{ path: string }>(result).path,
outputPath,
)
assert.ok(existsSync(outputPath), 'PDF file should exist in executionDir')
assert.ok(existsSync(outputPath), 'PDF file should exist in workingDir')
} finally {
await rm(executionDir, { recursive: true, force: true })
await rm(workingDir, { recursive: true, force: true })
}
})

it('save_screenshot still honors an explicit cwd override', async () => {
const executionDir = await mkdtemp(
join(tmpdir(), 'browseros-page-actions-'),
)
const workingDir = await mkdtemp(join(tmpdir(), 'browseros-page-actions-'))
const overrideDir = await mkdtemp(join(tmpdir(), 'browseros-page-actions-'))
const browser = createBrowserStub({
screenshot: async () => ({
Expand All @@ -95,7 +91,7 @@ describe('page action tools', () => {
const result = await executeTool(
save_screenshot,
{ page: 1, path: 'capture.png', cwd: overrideDir },
createToolContext(browser, executionDir),
createToolContext(browser, workingDir),
AbortSignal.timeout(1_000),
)

Expand All @@ -110,18 +106,18 @@ describe('page action tools', () => {
'Screenshot should exist in overrideDir',
)
assert.ok(
!existsSync(join(executionDir, 'capture.png')),
'Execution directory should not be used when cwd is provided',
!existsSync(join(workingDir, 'capture.png')),
'Working directory should not be used when cwd is provided',
)
} finally {
await rm(executionDir, { recursive: true, force: true })
await rm(workingDir, { recursive: true, force: true })
await rm(overrideDir, { recursive: true, force: true })
}
})

it('download_file resolves relative directories against the execution directory by default', async () => {
it('download_file resolves relative directories against the working directory by default', async () => {
const baseDir = await mkdtemp(join(tmpdir(), 'browseros-page-actions-'))
const executionDir = join(baseDir, 'execution')
const workingDir = join(baseDir, 'working')
let stagingDir: string | undefined
const browser = createBrowserStub({
downloadViaClick: async (
Expand All @@ -143,23 +139,23 @@ describe('page action tools', () => {
const result = await executeTool(
download_file,
{ page: 1, element: 7, path: '.' },
createToolContext(browser, executionDir),
createToolContext(browser, workingDir),
AbortSignal.timeout(1_000),
)

assert.ok(!result.isError, textOf(result))
const outputPath = join(executionDir, 'download.txt')
const outputPath = join(workingDir, 'download.txt')
const structured = structuredOf<{
directory: string
destinationPath: string
}>(result)
assert.strictEqual(structured.directory, executionDir)
assert.strictEqual(structured.directory, workingDir)
assert.strictEqual(structured.destinationPath, outputPath)
assert.ok(existsSync(outputPath), 'Download should land in executionDir')
assert.ok(existsSync(outputPath), 'Download should land in workingDir')
assert.ok(stagingDir, 'Download should use a staging directory')
assert.ok(
stagingDir.startsWith(join(executionDir, 'browseros-dl-')),
'Staging directory should be created inside executionDir',
stagingDir.startsWith(join(workingDir, 'browseros-dl-')),
'Staging directory should be created inside workingDir',
)
assert.ok(
!existsSync(stagingDir),
Expand Down
Loading