22import pytest
33from datetime import datetime
44from pathlib import Path
5+ from typing import List
56
67from .config import setup_logging , log_test_start , log_test_end
78from .config .logging_config import get_logger
89from .core import EnvironmentSwitcher
9- from .utils .lambdatest_reporter import LambdaTestReporter
10+ from .utils .cloud_reporter import CloudResultReporter
1011from .utils .screenshot import save_screenshot , save_page_source
1112
1213
1718
1819
1920_logging_setup = None
21+ _saved_failure_logs : List [Path ] = []
2022
2123
2224def pytest_configure (config ):
@@ -27,28 +29,27 @@ def pytest_configure(config):
2729 try :
2830 cli_env = getattr (config .option , "env" , None )
2931 if cli_env :
30- normalized_env = (
31- "lambdatest" if cli_env in ("lt" , "lambdatest" ) else "local"
32- )
33- os .environ ["CURRENT_TEST_ENVIRONMENT" ] = normalized_env
32+ os .environ ["CURRENT_TEST_ENVIRONMENT" ] = cli_env
3433 except Exception :
3534 # Do not block test runs if normalization fails
3635 pass
3736
3837 # Use YAML-based configuration
39- env_name = os .getenv ("CURRENT_TEST_ENVIRONMENT" , "lambdatest" )
38+ switcher = EnvironmentSwitcher ()
39+ env_name = (
40+ os .getenv ("CURRENT_TEST_ENVIRONMENT" ) or switcher .auto_detect_environment ()
41+ )
4042
4143 try :
42- switcher = EnvironmentSwitcher ()
4344 env_config = switcher .switch_to (env_name )
4445
4546 # Use directories from YAML config
4647 reports_dir = Path (env_config .directories .get ("reports" , "reports" ))
47- enable_xml_report = env_config .logging_config .get ("enable_xml_report" , True )
48- enable_html_report = env_config .logging_config .get ("enable_html_report" , True )
48+ enable_xml_report = env_config .logging .get ("enable_xml_report" , True )
49+ enable_html_report = env_config .logging .get ("enable_html_report" , True )
4950
5051 logger = get_logger ("conftest" )
51- logger .info (f"📁 Using reports directory from { env_name } config: { reports_dir } " )
52+ logger .info (" Using reports directory from %s config: %s" , env_name , reports_dir )
5253
5354 except Exception as e :
5455 # Simplified fallback using defaults
@@ -57,8 +58,8 @@ def pytest_configure(config):
5758 enable_html_report = True
5859
5960 logger = get_logger ("conftest" )
60- logger .warning (f"⚠️ Using default configuration: { e } " )
61- logger .warning ("💡 Ensure YAML config files are properly set up" )
61+ logger .warning (" Using default configuration: %s" , e )
62+ logger .warning ("Ensure YAML config files are properly set up" )
6263
6364 reports_dir .mkdir (exist_ok = True )
6465
@@ -77,19 +78,19 @@ def pytest_configure(config):
7778
7879 logger = _logging_setup ["loggers" ]["main" ] if _logging_setup else None
7980 if logger :
80- logger .info ("📊 Automatic report generation enabled:" )
81+ logger .info ("Automatic report generation enabled:" )
8182 if hasattr (config .option , "xmlpath" ) and config .option .xmlpath :
82- logger .info (f " 📄 XML Report: { config .option .xmlpath } " )
83+ logger .info (" XML report: %s" , config .option .xmlpath )
8384 if hasattr (config .option , "htmlpath" ) and config .option .htmlpath :
84- logger .info (f " 🌐 HTML Report: { config .option .htmlpath } " )
85+ logger .info (" HTML report: %s" , config .option .htmlpath )
8586
8687
8788def pytest_addoption (parser ):
8889 parser .addoption (
8990 "--env" ,
9091 action = "store" ,
91- default = "lt " ,
92- help = "Test environment: local or lt (LambdaTest )" ,
92+ default = "browserstack " ,
93+ help = "Test environment defined in config/environments (e.g. local, browserstack )" ,
9394 )
9495
9596
@@ -154,17 +155,19 @@ def pytest_runtest_makereport(item, call):
154155
155156 setattr (item , "rep_" + rep .when , rep )
156157
157- # Report setup/call/teardown so setup failures are reflected in LT
158+ # Report setup/call/teardown so setup failures are reflected in the cloud dashboard
158159 if rep .when in ("setup" , "call" , "teardown" ):
159160 try :
160- LambdaTestReporter .report_test_result (item , rep )
161+ CloudResultReporter .report_test_result (item , rep )
161162 except Exception as e :
162163 logger = get_logger ("session" )
163- logger .error (f"Failed to report test result to LambdaTest : { e } " )
164+ logger .error (f"Failed to report test result to cloud provider : { e } " )
164165
165166 # Get screenshot and page source artifacts
166167 try :
167- if getattr (rep , "failed" , False ) and not getattr (item , "_failure_artifacts_saved" , False ):
168+ if getattr (rep , "failed" , False ) and not getattr (
169+ item , "_failure_artifacts_saved" , False
170+ ):
168171 driver = None
169172 try :
170173 if hasattr (item , "instance" ) and hasattr (item .instance , "driver" ):
@@ -175,23 +178,36 @@ def pytest_runtest_makereport(item, call):
175178 if driver :
176179 # Resolve screenshots directory from environment config; fallback to 'screenshots'
177180 try :
178- env_name = os .getenv ("CURRENT_TEST_ENVIRONMENT" , "lambdatest" )
179- switcher = EnvironmentSwitcher ()
180- env_config = switcher .switch_to (env_name )
181- screenshots_dir = env_config .directories .get ("screenshots" , "screenshots" )
181+ env_switcher = EnvironmentSwitcher ()
182+ env_name = (
183+ os .getenv ("CURRENT_TEST_ENVIRONMENT" )
184+ or env_switcher .auto_detect_environment ()
185+ )
186+ env_config = env_switcher .switch_to (env_name )
187+ screenshots_dir = env_config .directories .get (
188+ "screenshots" , "screenshots"
189+ )
190+ logs_dir = env_config .directories .get ("logs" , "logs" )
182191 except Exception :
183192 screenshots_dir = "screenshots"
193+ logs_dir = "logs"
184194
185- test_id = getattr (item , "name" , "test" ) + (f"__{ rep .when } " if getattr (rep , "when" , None ) else "" )
195+ test_id = getattr (item , "name" , "test" ) + (
196+ f"__{ rep .when } " if getattr (rep , "when" , None ) else ""
197+ )
186198
187199 s_path = None
188200 x_path = None
189201 try :
190- s_path = save_screenshot (driver , str (screenshots_dir ), f"FAILED_{ test_id } " )
202+ s_path = save_screenshot (
203+ driver , str (screenshots_dir ), f"FAILED_{ test_id } "
204+ )
191205 except Exception :
192206 pass
193207 try :
194- x_path = save_page_source (driver , str (screenshots_dir ), f"FAILED_{ test_id } " )
208+ x_path = save_page_source (
209+ driver , str (screenshots_dir ), f"FAILED_{ test_id } "
210+ )
195211 except Exception :
196212 pass
197213
@@ -201,6 +217,56 @@ def pytest_runtest_makereport(item, call):
201217 if x_path :
202218 log .info (f"Saved failure page source: { x_path } " )
203219
220+ # Capture Appium server/logcat logs for deeper diagnostics
221+ log_paths : List [Path ] = []
222+ try :
223+ log_types = set (getattr (driver , "log_types" , []) or [])
224+ except Exception :
225+ log_types = set ()
226+
227+ desired_types = [
228+ log_type
229+ for log_type in ("server" , "logcat" )
230+ if log_type in log_types
231+ ]
232+ if desired_types :
233+ logs_root = Path (logs_dir )
234+ logs_root .mkdir (parents = True , exist_ok = True )
235+ for log_type in desired_types :
236+ try :
237+ entries = driver .get_log (log_type ) or []
238+ except Exception as err :
239+ log .warning (
240+ f"Failed to fetch '{ log_type } ' log for { test_id } : { err } "
241+ )
242+ continue
243+
244+ if not entries :
245+ continue
246+
247+ log_file = logs_root / f"FAILED_{ test_id } _{ log_type } .log"
248+ try :
249+ with log_file .open ("w" , encoding = "utf-8" ) as handle :
250+ for entry in entries :
251+ timestamp = (
252+ entry .get ("timestamp" )
253+ or entry .get ("time" )
254+ or ""
255+ )
256+ level = entry .get ("level" ) or ""
257+ message = entry .get ("message" ) or ""
258+ handle .write (f"{ timestamp } \t { level } \t { message } \n " )
259+ log .info (f"Saved failure { log_type } log: { log_file } " )
260+ log_paths .append (log_file )
261+ except Exception as err :
262+ log .warning (
263+ f"Failed to persist { log_type } log for { test_id } : { err } "
264+ )
265+
266+ if log_paths :
267+ global _saved_failure_logs
268+ _saved_failure_logs .extend (log_paths )
269+
204270 setattr (item , "_failure_artifacts_saved" , True )
205271 except Exception as e :
206272 log = get_logger ("conftest" )
@@ -221,37 +287,37 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config):
221287 total = passed + failed + skipped + errors
222288
223289 logger .info ("=" * 60 )
224- logger .info ("🎯 TEST EXECUTION SUMMARY" )
290+ logger .info ("TEST EXECUTION SUMMARY" )
225291 logger .info ("=" * 60 )
226- logger .info (f "Total Tests: { total } " )
227- logger .info (f"✅ Passed: { passed } " )
228- logger .info (f"❌ Failed: { failed } " )
229- logger .info (f"⏭️ Skipped: { skipped } " )
230- logger .info (f"💥 Errors: { errors } " )
292+ logger .info ("Total tests: %s" , total )
293+ logger .info (" Passed: %s" , passed )
294+ logger .info (" Failed: %s" , failed )
295+ logger .info (" Skipped: %s" , skipped )
296+ logger .info (" Errors: %s" , errors )
231297
232298 if total > 0 :
233299 success_rate = (passed / total ) * 100
234- logger .info (f"📊 Success Rate: { success_rate : .1f} %" )
300+ logger .info (" Success rate: % .1f%%" , success_rate )
235301
236302 logger .info ("Reports Generated:" )
237303 if hasattr (config .option , "xmlpath" ) and config .option .xmlpath :
238304 xml_file = Path (config .option .xmlpath )
239305 if xml_file .exists ():
240- logger .info (f " 📄 XML Report: { xml_file } " )
306+ logger .info (" XML report: %s" , xml_file )
241307 else :
242- logger .warning (f " ⚠️ XML Report expected but not found: { xml_file } " )
308+ logger .warning (" XML report expected but not found: %s" , xml_file )
243309
244310 if hasattr (config .option , "htmlpath" ) and config .option .htmlpath :
245311 html_file = Path (config .option .htmlpath )
246312 if html_file .exists ():
247- logger .info (f " 🌐 HTML Report: { html_file } " )
313+ logger .info (" HTML report: %s" , html_file )
248314 else :
249- logger .warning (f " ⚠️ HTML Report expected but not found: { html_file } " )
315+ logger .warning (" HTML report expected but not found: %s" , html_file )
250316
251317 logger .info ("=" * 60 )
252318
253319 if failed > 0 :
254- logger .warning (f"⚠️ { failed } test(s) failed. Check reports for details." )
320+ logger .warning ("%s test(s) failed. Check reports for details.", failed )
255321
256322 failed_tests = terminalreporter .stats .get ("failed" , [])
257323 for test_report in failed_tests [:5 ]:
@@ -262,13 +328,13 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config):
262328 if test_report .longrepr
263329 else "Unknown error"
264330 )
265- logger .error (f " ❌ { test_name } : { error_msg } " )
331+ logger .error (" %s: %s" , test_name , error_msg )
266332
267333 if len (failed_tests ) > 5 :
268334 logger .error (f" ... and { len (failed_tests ) - 5 } more failures" )
269335
270336 if errors > 0 :
271- logger .error (f"💥 { errors } test(s) had errors. Check reports for details." )
337+ logger .error ("%s test(s) had errors. Check reports for details.", errors )
272338
273339 error_tests = terminalreporter .stats .get ("error" , [])
274340 for test_report in error_tests [:3 ]:
@@ -279,12 +345,17 @@ def pytest_terminal_summary(terminalreporter, exitstatus, config):
279345 if test_report .longrepr
280346 else "Unknown error"
281347 )
282- logger .error (f " 💥 { test_name } : { error_msg } " )
348+ logger .error (" %s: %s" , test_name , error_msg )
283349
284350 if len (error_tests ) > 3 :
285351 logger .error (f" ... and { len (error_tests ) - 3 } more errors" )
286352
287353 if passed == total and total > 0 :
288- logger .info ("🎉 All tests passed successfully!" )
354+ logger .info ("All tests passed successfully!" )
289355
290356 logger .info ("=" * 60 )
357+
358+ if _saved_failure_logs :
359+ logger .info ("Failure log artifacts:" )
360+ for path in _saved_failure_logs :
361+ logger .info (" %s" , path )
0 commit comments