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