8
8
from stagehand .schemas import ExtractOptions
9
9
10
10
11
- skip_if_no_creds = pytest .mark .skipif (
12
- not (os .getenv ("BROWSERBASE_API_KEY" ) and os .getenv ("BROWSERBASE_PROJECT_ID" )),
13
- reason = "Browserbase credentials are not available for API integration tests" ,
14
- )
15
-
16
-
17
11
class Article (BaseModel ):
18
12
"""Schema for article extraction tests"""
19
13
title : str = Field (..., description = "The title of the article" )
20
14
summary : str = Field (None , description = "A brief summary or description of the article" )
21
15
22
16
23
- @pytest_asyncio .fixture (scope = "module" )
24
- @skip_if_no_creds
25
- async def stagehand_api ():
26
- """Provide a lightweight Stagehand instance pointing to the Browserbase API."""
27
- config = StagehandConfig (
28
- env = "BROWSERBASE" ,
29
- api_key = os .getenv ("BROWSERBASE_API_KEY" ),
30
- project_id = os .getenv ("BROWSERBASE_PROJECT_ID" ),
31
- headless = True ,
32
- verbose = 0 ,
33
- )
34
- sh = Stagehand (config = config )
35
- await sh .init ()
36
- yield sh
37
- await sh .close ()
17
+ class TestStagehandAPIIntegration :
18
+ """Integration tests for Stagehand Python SDK in BROWSERBASE API mode."""
38
19
20
+ @pytest .fixture (scope = "class" )
21
+ def browserbase_config (self ):
22
+ """Configuration for BROWSERBASE mode testing"""
23
+ return StagehandConfig (
24
+ env = "BROWSERBASE" ,
25
+ api_key = os .getenv ("BROWSERBASE_API_KEY" ),
26
+ project_id = os .getenv ("BROWSERBASE_PROJECT_ID" ),
27
+ model_name = "gpt-4o" ,
28
+ headless = False ,
29
+ verbose = 2 ,
30
+ model_client_options = {"apiKey" : os .getenv ("MODEL_API_KEY" ) or os .getenv ("OPENAI_API_KEY" )},
31
+ )
39
32
40
- @skip_if_no_creds
41
- @pytest .mark .integration
42
- @pytest .mark .api
43
- @pytest .mark .asyncio
44
- async def test_stagehand_api_initialization (stagehand_api ):
45
- """Ensure that Stagehand initializes correctly against the Browserbase API."""
46
- assert stagehand_api .session_id is not None
33
+ @pytest_asyncio .fixture
34
+ async def stagehand_api (self , browserbase_config ):
35
+ """Create a Stagehand instance for BROWSERBASE API testing"""
36
+ if not (os .getenv ("BROWSERBASE_API_KEY" ) and os .getenv ("BROWSERBASE_PROJECT_ID" )):
37
+ pytest .skip ("Browserbase credentials not available" )
38
+
39
+ stagehand = Stagehand (config = browserbase_config )
40
+ await stagehand .init ()
41
+ yield stagehand
42
+ await stagehand .close ()
47
43
44
+ @pytest .mark .asyncio
45
+ @pytest .mark .integration
46
+ @pytest .mark .api
47
+ @pytest .mark .skipif (
48
+ not (os .getenv ("BROWSERBASE_API_KEY" ) and os .getenv ("BROWSERBASE_PROJECT_ID" )),
49
+ reason = "Browserbase credentials are not available for API integration tests" ,
50
+ )
51
+ async def test_stagehand_api_initialization (self , stagehand_api ):
52
+ """Ensure that Stagehand initializes correctly against the Browserbase API."""
53
+ assert stagehand_api .session_id is not None
48
54
49
- @skip_if_no_creds
50
- @pytest .mark .integration
51
- @pytest .mark .api
52
- @pytest .mark .asyncio
53
- async def test_api_extract_functionality (stagehand_api ):
54
- """Test core extract functionality in API mode - extracted from e2e tests."""
55
- stagehand = stagehand_api
56
-
57
- # Navigate to a content-rich page
58
- await stagehand .page .goto ("https://news.ycombinator.com" )
59
-
60
- # Test simple text-based extraction
61
- titles_text = await stagehand .page .extract (
62
- "Extract the titles of the first 3 articles on the page as a JSON array"
55
+ @pytest .mark .asyncio
56
+ @pytest .mark .integration
57
+ @pytest .mark .api
58
+ @pytest .mark .skipif (
59
+ not (os .getenv ("BROWSERBASE_API_KEY" ) and os .getenv ("BROWSERBASE_PROJECT_ID" )),
60
+ reason = "Browserbase credentials are not available for API integration tests" ,
63
61
)
64
-
65
- # Verify extraction worked
66
- assert titles_text is not None
67
-
68
- # Test schema-based extraction
69
- extract_options = ExtractOptions (
70
- instruction = "Extract the first article's title and any available summary" ,
71
- schema_definition = Article
62
+ async def test_api_observe_and_act_workflow (self , stagehand_api ):
63
+ """Test core observe and act workflow in API mode - replicated from local tests."""
64
+ stagehand = stagehand_api
65
+
66
+ # Navigate to a form page for testing
67
+ await stagehand .page .goto ("https://httpbin.org/forms/post" )
68
+
69
+ # Test OBSERVE primitive: Find form elements
70
+ form_elements = await stagehand .page .observe ("Find all form input elements" )
71
+
72
+ # Verify observations
73
+ assert form_elements is not None
74
+ assert len (form_elements ) > 0
75
+
76
+ # Verify observation structure
77
+ for obs in form_elements :
78
+ assert hasattr (obs , "selector" )
79
+ assert obs .selector # Not empty
80
+
81
+ # Test ACT primitive: Fill form fields
82
+ await stagehand .page .act ("Fill the customer name field with 'API Integration Test'" )
83
+ await stagehand .page .act ("Fill the telephone field with '555-API'" )
84
+ await stagehand .
page .
act (
"Fill the email field with '[email protected] '" )
85
+
86
+ # Verify actions worked by observing filled fields
87
+ filled_fields = await stagehand .page .observe ("Find all filled form input fields" )
88
+ assert filled_fields is not None
89
+ assert len (filled_fields ) > 0
90
+
91
+ # Test interaction with specific elements
92
+ customer_field = await stagehand .page .observe ("Find the customer name input field" )
93
+ assert customer_field is not None
94
+ assert len (customer_field ) > 0
95
+
96
+ @pytest .mark .asyncio
97
+ @pytest .mark .integration
98
+ @pytest .mark .api
99
+ @pytest .mark .skipif (
100
+ not (os .getenv ("BROWSERBASE_API_KEY" ) and os .getenv ("BROWSERBASE_PROJECT_ID" )),
101
+ reason = "Browserbase credentials are not available for API integration tests" ,
102
+ )
103
+ async def test_api_basic_navigation_and_observe (self , stagehand_api ):
104
+ """Test basic navigation and observe functionality in API mode - replicated from local tests."""
105
+ stagehand = stagehand_api
106
+
107
+ # Navigate to a simple page
108
+ await stagehand .page .goto ("https://example.com" )
109
+
110
+ # Observe elements on the page
111
+ observations = await stagehand .page .observe ("Find all the links on the page" )
112
+
113
+ # Verify we got some observations
114
+ assert observations is not None
115
+ assert len (observations ) > 0
116
+
117
+ # Verify observation structure
118
+ for obs in observations :
119
+ assert hasattr (obs , "selector" )
120
+ assert obs .selector # Not empty
121
+
122
+ @pytest .mark .asyncio
123
+ @pytest .mark .integration
124
+ @pytest .mark .api
125
+ @pytest .mark .skipif (
126
+ not (os .getenv ("BROWSERBASE_API_KEY" ) and os .getenv ("BROWSERBASE_PROJECT_ID" )),
127
+ reason = "Browserbase credentials are not available for API integration tests" ,
72
128
)
73
-
74
- article_data = await stagehand .page .extract (extract_options )
75
- assert article_data is not None
76
-
77
- # Validate the extracted data structure (Browserbase format)
78
- if hasattr (article_data , 'data' ) and article_data .data :
79
- # BROWSERBASE mode format
80
- article = Article .model_validate (article_data .data )
81
- assert article .title
82
- assert len (article .title ) > 0
83
- elif hasattr (article_data , 'title' ):
84
- # Fallback format
85
- article = Article .model_validate (article_data .model_dump ())
86
- assert article .title
87
- assert len (article .title ) > 0
88
-
89
- # Verify API session is active
90
- assert stagehand .session_id is not None
129
+ async def test_api_extraction_functionality (self , stagehand_api ):
130
+ """Test extraction functionality in API mode - replicated from local tests."""
131
+ stagehand = stagehand_api
132
+
133
+ # Navigate to a content-rich page
134
+ await stagehand .page .goto ("https://news.ycombinator.com" )
135
+
136
+ # Test simple text-based extraction
137
+ titles_text = await stagehand .page .extract (
138
+ "Extract the titles of the first 3 articles on the page as a JSON array"
139
+ )
140
+
141
+ # Verify extraction worked
142
+ assert titles_text is not None
143
+
144
+ # Test schema-based extraction
145
+ extract_options = ExtractOptions (
146
+ instruction = "Extract the first article's title and any available summary" ,
147
+ schema_definition = Article
148
+ )
149
+
150
+ article_data = await stagehand .page .extract (extract_options )
151
+ assert article_data is not None
152
+
153
+ # Validate the extracted data structure (Browserbase format)
154
+ if hasattr (article_data , 'data' ) and article_data .data :
155
+ # BROWSERBASE mode format
156
+ article = Article .model_validate (article_data .data )
157
+ assert article .title
158
+ assert len (article .title ) > 0
159
+ elif hasattr (article_data , 'title' ):
160
+ # Fallback format
161
+ article = Article .model_validate (article_data .model_dump ())
162
+ assert article .title
163
+ assert len (article .title ) > 0
164
+
165
+ # Verify API session is active
166
+ assert stagehand .session_id is not None
0 commit comments