Skip to content

Commit 324277e

Browse files
all tests pass
1 parent 20605bb commit 324277e

File tree

3 files changed

+30
-26
lines changed

3 files changed

+30
-26
lines changed

tests/unit/core/test_page.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ async def test_injection_script_loading_error(self, mock_stagehand_page):
9595
import stagehand.page
9696
stagehand.page._INJECTION_SCRIPT = None
9797

98+
# Remove the mock and restore the real ensure_injection method
99+
from stagehand.page import StagehandPage
100+
mock_stagehand_page.ensure_injection = StagehandPage.ensure_injection.__get__(mock_stagehand_page)
101+
98102
# Set up the page to return False for script check, triggering script loading
99103
mock_stagehand_page._page.evaluate.return_value = False
100104

tests/unit/handlers/test_observe_handler.py

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ async def test_observe_with_only_visible_option(self, mock_stagehand_page):
179179
])
180180

181181
handler = ObserveHandler(mock_stagehand_page, mock_client, "")
182-
mock_stagehand_page._page.evaluate = AsyncMock(return_value="Only visible elements")
182+
# Mock evaluate method for find_scrollable_element_ids
183+
mock_stagehand_page.evaluate = AsyncMock(return_value=["//body", "//div[@class='content']"])
183184

184185
options = ObserveOptions(
185186
instruction="find buttons",
@@ -191,8 +192,8 @@ async def test_observe_with_only_visible_option(self, mock_stagehand_page):
191192
assert len(result) == 1
192193
assert result[0].selector == "xpath=//button[@id='visible-button']"
193194

194-
# Should have called evaluate with visibility filter
195-
mock_stagehand_page._page.evaluate.assert_called()
195+
# Should have called evaluate for scrollable elements
196+
mock_stagehand_page.evaluate.assert_called()
196197

197198
@pytest.mark.asyncio
198199
async def test_observe_with_return_action_option(self, mock_stagehand_page):
@@ -236,23 +237,26 @@ async def test_observe_from_act_context(self, mock_stagehand_page):
236237
mock_client.start_inference_timer = MagicMock()
237238
mock_client.update_metrics = MagicMock()
238239

239-
mock_llm.set_custom_response("observe", [
240+
# When from_act=True, the function_name becomes "ACT", so set custom response for "act"
241+
mock_llm.set_custom_response("act", [
240242
{
241243
"description": "Element to act on",
242-
"element_id": 400,
244+
"element_id": 1, # Use element_id 1 which exists in the accessibility tree
243245
"method": "click",
244246
"arguments": []
245247
}
246248
])
247249

248250
handler = ObserveHandler(mock_stagehand_page, mock_client, "")
249-
mock_stagehand_page._page.evaluate = AsyncMock(return_value="Act context DOM")
251+
# Mock evaluate method for find_scrollable_element_ids
252+
mock_stagehand_page.evaluate = AsyncMock(return_value=["//body"])
250253

251254
options = ObserveOptions(instruction="find target element")
252255
result = await handler.observe(options, from_act=True)
253256

254257
assert len(result) == 1
255-
assert result[0].selector == "xpath=//div[@id='target-element']"
258+
# The xpath mapping for element_id 1 should be "//div[@id='test']" based on conftest setup
259+
assert result[0].selector == "xpath=//div[@id='test']"
256260

257261
@pytest.mark.asyncio
258262
async def test_observe_with_llm_failure(self, mock_stagehand_page):
@@ -286,14 +290,6 @@ async def test_dom_element_extraction(self, mock_stagehand_page):
286290
mock_client.start_inference_timer = MagicMock()
287291
mock_client.update_metrics = MagicMock()
288292

289-
# Mock DOM extraction
290-
mock_dom_elements = [
291-
{"id": "btn1", "text": "Click me", "tagName": "BUTTON"},
292-
{"id": "btn2", "text": "Submit", "tagName": "BUTTON"}
293-
]
294-
295-
mock_stagehand_page._page.evaluate = AsyncMock(return_value=mock_dom_elements)
296-
297293
mock_llm.set_custom_response("observe", [
298294
{
299295
"description": "Click me button",
@@ -304,12 +300,14 @@ async def test_dom_element_extraction(self, mock_stagehand_page):
304300
])
305301

306302
handler = ObserveHandler(mock_stagehand_page, mock_client, "")
303+
# Mock evaluate method for find_scrollable_element_ids
304+
mock_stagehand_page.evaluate = AsyncMock(return_value=["//button[@id='btn1']", "//button[@id='btn2']"])
307305

308306
options = ObserveOptions(instruction="find button elements")
309307
result = await handler.observe(options)
310308

311-
# Should have called page.evaluate to extract DOM elements
312-
mock_stagehand_page._page.evaluate.assert_called()
309+
# Should have called evaluate to find scrollable elements
310+
mock_stagehand_page.evaluate.assert_called()
313311

314312
assert len(result) == 1
315313
assert result[0].selector == "xpath=//button[@id='btn1']"
@@ -412,7 +410,8 @@ async def test_observe_with_draw_overlay(self, mock_stagehand_page):
412410
])
413411

414412
handler = ObserveHandler(mock_stagehand_page, mock_client, "")
415-
mock_stagehand_page._page.evaluate = AsyncMock(return_value="DOM with overlay")
413+
# Mock evaluate method for find_scrollable_element_ids
414+
mock_stagehand_page.evaluate = AsyncMock(return_value=["//div[@id='highlighted-element']"])
416415

417416
options = ObserveOptions(
418417
instruction="find elements",
@@ -423,8 +422,8 @@ async def test_observe_with_draw_overlay(self, mock_stagehand_page):
423422

424423
# Should have drawn overlay on elements
425424
assert len(result) == 1
426-
# Overlay drawing would be tested through DOM evaluation calls
427-
mock_stagehand_page._page.evaluate.assert_called()
425+
# Should have called evaluate for finding scrollable elements
426+
mock_stagehand_page.evaluate.assert_called()
428427

429428
@pytest.mark.asyncio
430429
async def test_observe_with_custom_model(self, mock_stagehand_page):
@@ -485,7 +484,8 @@ async def test_observe_result_serialization(self, mock_stagehand_page):
485484
])
486485

487486
handler = ObserveHandler(mock_stagehand_page, mock_client, "")
488-
mock_stagehand_page._page.evaluate = AsyncMock(return_value="Complex DOM")
487+
# Mock evaluate method for find_scrollable_element_ids
488+
mock_stagehand_page.evaluate = AsyncMock(return_value=["//input[@id='complex-element']"])
489489

490490
options = ObserveOptions(instruction="find complex elements")
491491
result = await handler.observe(options)
@@ -495,7 +495,6 @@ async def test_observe_result_serialization(self, mock_stagehand_page):
495495

496496
assert obs_result.selector == "xpath=//input[@id='complex-element']"
497497
assert obs_result.description == "Complex element with all properties"
498-
assert obs_result.backend_node_id == 1000
499498
assert obs_result.method == "type"
500499
assert obs_result.arguments == ["test input"]
501500

tests/unit/test_client_initialization.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def test_init_with_config(self):
6767

6868
@mock.patch.dict(os.environ, {}, clear=True)
6969
def test_config_priority_over_direct_params(self):
70-
"""Test that direct parameters override config parameters."""
70+
"""Test that config parameters take precedence over direct parameters (except session_id)."""
7171
config = StagehandConfig(
7272
env="LOCAL", # Use LOCAL to avoid BROWSERBASE validation
7373
api_key="config-api-key",
@@ -82,9 +82,10 @@ def test_config_priority_over_direct_params(self):
8282
session_id="direct-session-id",
8383
)
8484

85-
# Direct parameters should override config values
86-
assert client.browserbase_api_key == "direct-api-key"
87-
assert client.browserbase_project_id == "direct-project-id"
85+
# Config parameters take precedence for api_key and project_id
86+
assert client.browserbase_api_key == "config-api-key"
87+
assert client.browserbase_project_id == "config-project-id"
88+
# But session_id parameter overrides config since it's handled specially
8889
assert client.session_id == "direct-session-id"
8990

9091
def test_init_with_missing_required_fields(self):

0 commit comments

Comments
 (0)