@@ -203,3 +203,92 @@ async def mock_create_session():
203203 # Call _create_session and expect error
204204 with pytest .raises (RuntimeError , match = "Invalid response format" ):
205205 await client ._create_session ()
206+
207+ @pytest .mark .asyncio
208+ @mock .patch ("stagehand.main.async_playwright" )
209+ async def test_init_playwright_in_thread (self , mock_async_playwright ):
210+ """Test that playwright initialization works properly in a separate thread."""
211+ # Create a mock playwright instance
212+ mock_playwright_instance = mock .AsyncMock ()
213+ mock_playwright_instance .stop = mock .AsyncMock ()
214+ mock_playwright_instance .chromium = mock .MagicMock ()
215+
216+ # Mock the async_playwright().start() to return our mock instance
217+ mock_async_playwright_start = mock .AsyncMock (return_value = mock_playwright_instance )
218+ mock_async_playwright .return_value .start = mock_async_playwright_start
219+
220+ # Create a Stagehand client with LOCAL env
221+ config = StagehandConfig (env = "LOCAL" )
222+ client = Stagehand (config = config )
223+
224+ # Test the threaded playwright initialization
225+ result = await client ._init_playwright_in_thread ()
226+
227+ # Verify that the playwright instance was returned
228+ assert result is mock_playwright_instance
229+
230+ # Verify that async_playwright().start() was called
231+ mock_async_playwright_start .assert_called_once ()
232+
233+ # Verify the result has the expected attributes
234+ assert hasattr (result , 'chromium' )
235+ assert hasattr (result , 'stop' )
236+
237+ @pytest .mark .asyncio
238+ @mock .patch ("stagehand.main.async_playwright" )
239+ async def test_init_playwright_in_thread_handles_exceptions (self , mock_async_playwright ):
240+ """Test that threaded playwright initialization properly handles exceptions."""
241+ # Mock async_playwright().start() to raise an exception
242+ mock_async_playwright .return_value .start .side_effect = Exception ("Test exception" )
243+
244+ # Create a Stagehand client with LOCAL env
245+ config = StagehandConfig (env = "LOCAL" )
246+ client = Stagehand (config = config )
247+
248+ # Test that the method raises a RuntimeError with our exception message
249+ with pytest .raises (RuntimeError , match = "Failed to initialize Playwright in thread" ):
250+ await client ._init_playwright_in_thread ()
251+
252+ @pytest .mark .asyncio
253+ @mock .patch ("stagehand.main.cleanup_browser_resources" )
254+ @mock .patch ("stagehand.main.connect_local_browser" )
255+ @mock .patch .object (Stagehand , "_init_playwright_in_thread" )
256+ async def test_init_uses_threaded_playwright (
257+ self , mock_init_playwright , mock_connect_local , mock_cleanup
258+ ):
259+ """Test that the main init() method uses threaded playwright initialization."""
260+ # Set up mocks
261+ mock_playwright_instance = mock .AsyncMock ()
262+ mock_init_playwright .return_value = mock_playwright_instance
263+
264+ # Mock the browser connection to avoid complex setup
265+ mock_browser = mock .AsyncMock ()
266+ mock_context = mock .AsyncMock ()
267+ mock_stagehand_context = mock .MagicMock ()
268+ mock_page = mock .MagicMock ()
269+ mock_page ._page = mock .AsyncMock ()
270+
271+ mock_connect_local .return_value = (
272+ mock_browser ,
273+ mock_context ,
274+ mock_stagehand_context ,
275+ mock_page ,
276+ None # temp_user_data_dir
277+ )
278+
279+ # Create a Stagehand client with LOCAL env
280+ config = StagehandConfig (env = "LOCAL" )
281+ client = Stagehand (config = config )
282+
283+ # Initialize the client
284+ await client .init ()
285+
286+ # Verify that threaded playwright initialization was called
287+ mock_init_playwright .assert_called_once ()
288+
289+ # Verify that the client is properly initialized
290+ assert client ._initialized is True
291+ assert client ._playwright is mock_playwright_instance
292+
293+ # Clean up
294+ await client .close ()
0 commit comments