Spaces:
Sleeping
Sleeping
File size: 6,576 Bytes
d7b3d84 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | """Test all recording and save functionality for Agent and BrowserSession."""
from pathlib import Path
import pytest
from browser_use import Agent, AgentHistoryList
from browser_use.browser import BrowserProfile, BrowserSession
from tests.ci.conftest import create_mock_llm
@pytest.fixture
def test_dir(tmp_path):
"""Create a test directory that gets cleaned up after each test."""
test_path = tmp_path / 'test_recordings'
test_path.mkdir(exist_ok=True)
yield test_path
@pytest.fixture
async def httpserver_url(httpserver):
"""Simple test page."""
# Use expect_ordered_request with multiple handlers to handle repeated requests
for _ in range(10): # Allow up to 10 requests to the same URL
httpserver.expect_ordered_request('/').respond_with_data(
"""
<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
</head>
<body>
<h1>Test Recording Page</h1>
<input type="text" id="search" placeholder="Search here" />
<button type="button" id="submit">Submit</button>
</body>
</html>
""",
content_type='text/html',
)
return httpserver.url_for('/')
@pytest.fixture
def llm():
"""Create mocked LLM instance for tests."""
return create_mock_llm()
@pytest.fixture
def interactive_llm(httpserver_url):
"""Create mocked LLM that navigates to page and interacts with elements."""
actions = [
# First action: Navigate to the page
f"""
{{
"thinking": "null",
"evaluation_previous_goal": "Starting the task",
"memory": "Need to navigate to the test page",
"next_goal": "Navigate to the URL",
"action": [
{{
"navigate": {{
"url": "{httpserver_url}",
"new_tab": false
}}
}}
]
}}
""",
# Second action: Click in the search box
"""
{
"thinking": "null",
"evaluation_previous_goal": "Successfully navigated to the page",
"memory": "Page loaded, can see search box and submit button",
"next_goal": "Click on the search box to focus it",
"action": [
{
"click": {
"index": 0
}
}
]
}
""",
# Third action: Type text in the search box
"""
{
"thinking": "null",
"evaluation_previous_goal": "Clicked on search box",
"memory": "Search box is focused and ready for input",
"next_goal": "Type 'test' in the search box",
"action": [
{
"input_text": {
"index": 0,
"text": "test"
}
}
]
}
""",
# Fourth action: Click submit button
"""
{
"thinking": "null",
"evaluation_previous_goal": "Typed 'test' in search box",
"memory": "Text 'test' has been entered successfully",
"next_goal": "Click the submit button to complete the task",
"action": [
{
"click": {
"index": 1
}
}
]
}
""",
# Fifth action: Done - task completed
"""
{
"thinking": "null",
"evaluation_previous_goal": "Clicked the submit button",
"memory": "Successfully navigated to the page, typed 'test' in the search box, and clicked submit",
"next_goal": "Task completed",
"action": [
{
"done": {
"text": "Task completed - typed 'test' in search box and clicked submit",
"success": true
}
}
]
}
""",
]
return create_mock_llm(actions)
class TestAgentRecordings:
"""Test Agent save_conversation_path and generate_gif parameters."""
@pytest.mark.parametrize('path_type', ['with_slash', 'without_slash', 'deep_directory'])
async def test_save_conversation_path(self, test_dir, httpserver_url, llm, path_type):
"""Test saving conversation with different path types."""
if path_type == 'with_slash':
conversation_path = test_dir / 'logs' / 'conversation'
elif path_type == 'without_slash':
conversation_path = test_dir / 'logs'
else: # deep_directory
conversation_path = test_dir / 'logs' / 'deep' / 'directory' / 'conversation'
browser_session = BrowserSession(browser_profile=BrowserProfile(headless=True, disable_security=True, user_data_dir=None))
await browser_session.start()
try:
agent = Agent(
task=f'go to {httpserver_url} and type "test" in the search box',
llm=llm,
browser_session=browser_session,
save_conversation_path=str(conversation_path),
)
history: AgentHistoryList = await agent.run(max_steps=2)
result = history.final_result()
assert result is not None
# Check that the conversation directory and files were created
assert conversation_path.exists(), f'{path_type}: conversation directory was not created'
# Files are now always created as conversation_<agent_id>_<step>.txt inside the directory
conversation_files = list(conversation_path.glob('conversation_*.txt'))
assert len(conversation_files) > 0, f'{path_type}: conversation file was not created in {conversation_path}'
finally:
await browser_session.kill()
@pytest.mark.skip(reason='TODO: fix')
@pytest.mark.parametrize('generate_gif', [False, True, 'custom_path'])
async def test_generate_gif(self, test_dir, httpserver_url, llm, generate_gif):
"""Test GIF generation with different settings."""
# Clean up any existing GIFs first
for gif in Path.cwd().glob('agent_*.gif'):
gif.unlink()
gif_param = generate_gif
expected_gif_path = None
if generate_gif == 'custom_path':
expected_gif_path = test_dir / 'custom_agent.gif'
gif_param = str(expected_gif_path)
browser_session = BrowserSession(browser_profile=BrowserProfile(headless=True, disable_security=True, user_data_dir=None))
await browser_session.start()
try:
agent = Agent(
task=f'go to {httpserver_url}',
llm=llm,
browser_session=browser_session,
generate_gif=gif_param,
)
history: AgentHistoryList = await agent.run(max_steps=2)
result = history.final_result()
assert result is not None
# Check GIF creation
if generate_gif is False:
gif_files = list(Path.cwd().glob('*.gif'))
assert len(gif_files) == 0, 'GIF file was created when generate_gif=False'
elif generate_gif is True:
# With mock LLM that doesn't navigate, all screenshots will be about:blank placeholders
# So no GIF will be created (this is expected behavior)
gif_files = list(Path.cwd().glob('agent_history.gif'))
assert len(gif_files) == 0, 'GIF should not be created when all screenshots are placeholders'
else: # custom_path
assert expected_gif_path is not None, 'expected_gif_path should be set for custom_path'
# With mock LLM that doesn't navigate, no GIF will be created
assert not expected_gif_path.exists(), 'GIF should not be created when all screenshots are placeholders'
finally:
await browser_session.kill()
|