1+ """
2+ Regression test for act timeout functionality.
3+
4+ This test verifies that the timeout mechanism works correctly for act operations,
5+ based on the TypeScript expect_act_timeout evaluation.
6+ """
7+
8+ import os
9+ import pytest
10+ import pytest_asyncio
11+
12+ from stagehand import Stagehand , StagehandConfig
13+
14+
15+ class TestActTimeout :
16+ """Regression test for act timeout functionality"""
17+
18+ @pytest .fixture (scope = "class" )
19+ def local_config (self ):
20+ """Configuration for LOCAL mode testing"""
21+ return StagehandConfig (
22+ env = "LOCAL" ,
23+ model_name = "gpt-4o-mini" ,
24+ headless = True ,
25+ verbose = 1 ,
26+ dom_settle_timeout_ms = 2000 ,
27+ model_client_options = {"apiKey" : os .getenv ("MODEL_API_KEY" ) or os .getenv ("OPENAI_API_KEY" )},
28+ )
29+
30+ @pytest .fixture (scope = "class" )
31+ def browserbase_config (self ):
32+ """Configuration for BROWSERBASE mode testing"""
33+ return StagehandConfig (
34+ env = "BROWSERBASE" ,
35+ api_key = os .getenv ("BROWSERBASE_API_KEY" ),
36+ project_id = os .getenv ("BROWSERBASE_PROJECT_ID" ),
37+ model_name = "gpt-4o" ,
38+ headless = False ,
39+ verbose = 2 ,
40+ model_client_options = {"apiKey" : os .getenv ("MODEL_API_KEY" ) or os .getenv ("OPENAI_API_KEY" )},
41+ )
42+
43+ @pytest_asyncio .fixture
44+ async def local_stagehand (self , local_config ):
45+ """Create a Stagehand instance for LOCAL testing"""
46+ stagehand = Stagehand (config = local_config )
47+ await stagehand .init ()
48+ yield stagehand
49+ await stagehand .close ()
50+
51+ @pytest_asyncio .fixture
52+ async def browserbase_stagehand (self , browserbase_config ):
53+ """Create a Stagehand instance for BROWSERBASE testing"""
54+ if not (os .getenv ("BROWSERBASE_API_KEY" ) and os .getenv ("BROWSERBASE_PROJECT_ID" )):
55+ pytest .skip ("Browserbase credentials not available" )
56+
57+ stagehand = Stagehand (config = browserbase_config )
58+ await stagehand .init ()
59+ yield stagehand
60+ await stagehand .close ()
61+
62+ @pytest .mark .asyncio
63+ @pytest .mark .regression
64+ @pytest .mark .local
65+ async def test_expect_act_timeout_local (self , local_stagehand ):
66+ """
67+ Regression test: expect_act_timeout
68+
69+ Mirrors the TypeScript expect_act_timeout evaluation:
70+ - Navigate to docs.stagehand.dev
71+ - Attempt action with 1 second timeout
72+ - Expect the action to fail due to timeout
73+ """
74+ stagehand = local_stagehand
75+
76+ await stagehand .page .goto ("https://docs.stagehand.dev" )
77+
78+ result = await stagehand .page .act (
79+ "Click the button with id 'nonexistent-button-that-will-never-exist-12345'" ,
80+ timeout_ms = 1000 # 1 second timeout
81+ )
82+
83+ # Test passes if the action failed (due to timeout or element not found)
84+ # This mirrors the TypeScript: _success: !result.success
85+ assert not result .success , "Action should have failed due to timeout or missing element"
86+
87+ @pytest .mark .asyncio
88+ @pytest .mark .regression
89+ @pytest .mark .api
90+ @pytest .mark .skipif (
91+ not (os .getenv ("BROWSERBASE_API_KEY" ) and os .getenv ("BROWSERBASE_PROJECT_ID" )),
92+ reason = "Browserbase credentials not available"
93+ )
94+ async def test_expect_act_timeout_browserbase (self , browserbase_stagehand ):
95+ """
96+ Regression test: expect_act_timeout (Browserbase)
97+
98+ Same test as local but running in Browserbase environment.
99+ """
100+ stagehand = browserbase_stagehand
101+
102+ await stagehand .page .goto ("https://docs.stagehand.dev" )
103+
104+ result = await stagehand .page .act (
105+ "Click the button with id 'nonexistent-button-that-will-never-exist-12345'" ,
106+ timeout_ms = 1000 # 1 second timeout
107+ )
108+
109+ # Test passes if the action failed (due to timeout or element not found)
110+ assert not result .success , "Action should have failed due to timeout or missing element"
0 commit comments