1+ """Test the LivePageProxy functionality"""
2+
3+ import asyncio
4+ import pytest
5+ from unittest .mock import AsyncMock , MagicMock , patch
6+ from stagehand .main import LivePageProxy , Stagehand
7+ from stagehand .page import StagehandPage
8+
9+
10+ @pytest .mark .asyncio
11+ async def test_live_page_proxy_basic_delegation (mock_stagehand_config ):
12+ """Test that LivePageProxy delegates to the active page"""
13+ # Create a Stagehand instance
14+ stagehand = Stagehand (config = mock_stagehand_config )
15+
16+ # Mock pages
17+ mock_original_page = MagicMock (spec = StagehandPage )
18+ mock_original_page .url = "https://original.com"
19+ mock_original_page .title = AsyncMock (return_value = "Original Page" )
20+
21+ mock_active_page = MagicMock (spec = StagehandPage )
22+ mock_active_page .url = "https://active.com"
23+ mock_active_page .title = AsyncMock (return_value = "Active Page" )
24+
25+ # Set up the pages
26+ stagehand ._original_page = mock_original_page
27+ stagehand ._active_page = mock_active_page
28+ stagehand ._initialized = True
29+
30+ # Get the proxy
31+ proxy = stagehand .page
32+
33+ # Test that it delegates to the active page
34+ assert proxy .url == "https://active.com"
35+ title = await proxy .title ()
36+ assert title == "Active Page"
37+
38+
39+ @pytest .mark .asyncio
40+ async def test_live_page_proxy_falls_back_to_original (mock_stagehand_config ):
41+ """Test that LivePageProxy falls back to original page when no active page"""
42+ # Create a Stagehand instance
43+ stagehand = Stagehand (config = mock_stagehand_config )
44+
45+ # Mock original page only
46+ mock_original_page = MagicMock (spec = StagehandPage )
47+ mock_original_page .url = "https://original.com"
48+
49+ # Set up the pages
50+ stagehand ._original_page = mock_original_page
51+ stagehand ._active_page = None
52+ stagehand ._initialized = True
53+
54+ # Get the proxy
55+ proxy = stagehand .page
56+
57+ # Test that it delegates to the original page
58+ assert proxy .url == "https://original.com"
59+
60+
61+ @pytest .mark .asyncio
62+ async def test_live_page_proxy_page_stability (mock_stagehand_config ):
63+ """Test that LivePageProxy waits for page stability on async operations"""
64+ # Create a Stagehand instance
65+ stagehand = Stagehand (config = mock_stagehand_config )
66+
67+ # Track lock acquisition
68+ lock_acquired = False
69+ lock_released = False
70+
71+ class TestLock :
72+ async def __aenter__ (self ):
73+ nonlocal lock_acquired
74+ lock_acquired = True
75+ await asyncio .sleep (0.1 ) # Simulate some work
76+ return self
77+
78+ async def __aexit__ (self , * args ):
79+ nonlocal lock_released
80+ lock_released = True
81+
82+ stagehand ._page_switch_lock = TestLock ()
83+
84+ # Mock page with async method
85+ mock_page = MagicMock (spec = StagehandPage )
86+ mock_page .click = AsyncMock (return_value = None )
87+
88+ # Set up the pages
89+ stagehand ._original_page = mock_page
90+ stagehand ._active_page = mock_page
91+ stagehand ._initialized = True
92+
93+ # Get the proxy
94+ proxy = stagehand .page
95+
96+ # Call an async method (should wait for stability)
97+ await proxy .click ("button" )
98+
99+ # Verify lock was acquired and released
100+ assert lock_acquired
101+ assert lock_released
102+ mock_page .click .assert_called_once_with ("button" )
103+
104+
105+ @pytest .mark .asyncio
106+ async def test_live_page_proxy_navigation_no_stability_check (mock_stagehand_config ):
107+ """Test that navigation methods don't wait for page stability"""
108+ # Create a Stagehand instance
109+ stagehand = Stagehand (config = mock_stagehand_config )
110+
111+ # Track lock acquisition (should not happen)
112+ lock_acquired = False
113+
114+ class TestLock :
115+ async def __aenter__ (self ):
116+ nonlocal lock_acquired
117+ lock_acquired = True
118+ return self
119+
120+ async def __aexit__ (self , * args ):
121+ pass
122+
123+ stagehand ._page_switch_lock = TestLock ()
124+
125+ # Mock page with navigation methods
126+ mock_page = MagicMock (spec = StagehandPage )
127+ mock_page .goto = AsyncMock (return_value = None )
128+ mock_page .reload = AsyncMock (return_value = None )
129+ mock_page .go_back = AsyncMock (return_value = None )
130+ mock_page .go_forward = AsyncMock (return_value = None )
131+
132+ # Set up the pages
133+ stagehand ._original_page = mock_page
134+ stagehand ._active_page = mock_page
135+ stagehand ._initialized = True
136+
137+ # Get the proxy
138+ proxy = stagehand .page
139+
140+ # Call navigation methods (should NOT wait for stability)
141+ await proxy .goto ("https://example.com" )
142+ await proxy .reload ()
143+ await proxy .go_back ()
144+ await proxy .go_forward ()
145+
146+ # Verify lock was NOT acquired
147+ assert not lock_acquired
148+
149+ # Verify methods were called
150+ mock_page .goto .assert_called_once_with ("https://example.com" )
151+ mock_page .reload .assert_called_once ()
152+ mock_page .go_back .assert_called_once ()
153+ mock_page .go_forward .assert_called_once ()
154+
155+
156+ @pytest .mark .asyncio
157+ async def test_live_page_proxy_dynamic_page_switching (mock_stagehand_config ):
158+ """Test that LivePageProxy dynamically switches between pages"""
159+ # Create a Stagehand instance
160+ stagehand = Stagehand (config = mock_stagehand_config )
161+
162+ # Mock pages
163+ page1 = MagicMock (spec = StagehandPage )
164+ page1 .url = "https://page1.com"
165+
166+ page2 = MagicMock (spec = StagehandPage )
167+ page2 .url = "https://page2.com"
168+
169+ # Set up initial state
170+ stagehand ._original_page = page1
171+ stagehand ._active_page = page1
172+ stagehand ._initialized = True
173+
174+ # Get the proxy
175+ proxy = stagehand .page
176+
177+ # Initially points to page1
178+ assert proxy .url == "https://page1.com"
179+
180+ # Switch active page
181+ stagehand ._active_page = page2
182+
183+ # Now points to page2 without creating a new proxy
184+ assert proxy .url == "https://page2.com"
185+
186+
187+ def test_live_page_proxy_no_page_error (mock_stagehand_config ):
188+ """Test that LivePageProxy raises error when no page is available"""
189+ # Create a Stagehand instance
190+ stagehand = Stagehand (config = mock_stagehand_config )
191+
192+ # No pages set
193+ stagehand ._original_page = None
194+ stagehand ._active_page = None
195+ stagehand ._initialized = True
196+
197+ # Get the proxy
198+ proxy = stagehand .page
199+
200+ # Accessing attributes should raise RuntimeError
201+ with pytest .raises (RuntimeError , match = "No active page available" ):
202+ _ = proxy .url
203+
204+
205+ def test_live_page_proxy_not_initialized (mock_stagehand_config ):
206+ """Test that page property returns None when not initialized"""
207+ # Create a Stagehand instance
208+ stagehand = Stagehand (config = mock_stagehand_config )
209+ stagehand ._initialized = False
210+
211+ # Should return None
212+ assert stagehand .page is None
0 commit comments