99import logging
1010from bs4 import BeautifulSoup
1111import atexit
12+ from datetime import datetime
13+
14+ # Create screenshots directory if it doesn't exist
15+ SCREENSHOTS_DIR = os .path .join (os .path .dirname (__file__ ), "screenshots" )
16+ os .makedirs (SCREENSHOTS_DIR , exist_ok = True )
17+ print (f"Screenshots will be saved to: { SCREENSHOTS_DIR } " ) # Debug line
1218
1319@pytest .fixture (scope = "session" )
1420def login_logout ():
@@ -51,9 +57,45 @@ def pytest_html_report_title(report):
5157
5258@pytest .hookimpl (hookwrapper = True )
5359def pytest_runtest_makereport (item , call ):
60+ """Generate test report with logs, subtest details, and screenshots on failure"""
5461 outcome = yield
5562 report = outcome .get_result ()
5663
64+ # Capture screenshot on failure
65+ if report .when == "call" and report .failed :
66+ # Get the page fixture if it exists
67+ if "login_logout" in item .fixturenames :
68+ page = item .funcargs .get ("login_logout" )
69+ if page :
70+ try :
71+ # Generate screenshot filename with timestamp
72+ timestamp = datetime .now ().strftime ("%Y%m%d_%H%M%S" )
73+ test_name = item .name .replace (" " , "_" ).replace ("/" , "_" )
74+ screenshot_name = f"screenshot_{ test_name } _{ timestamp } .png"
75+ screenshot_path = os .path .join (SCREENSHOTS_DIR , screenshot_name )
76+
77+ # Take screenshot
78+ page .screenshot (path = screenshot_path )
79+
80+ # Add screenshot link to report
81+ if not hasattr (report , 'extra' ):
82+ report .extra = []
83+
84+ # Add screenshot as a link in the Links column
85+ # Use relative path from report.html location
86+ relative_path = os .path .relpath (
87+ screenshot_path ,
88+ os .path .dirname (os .path .abspath ("report.html" ))
89+ )
90+
91+ # pytest-html expects this format for extras
92+ from pytest_html import extras
93+ report .extra .append (extras .url (relative_path , name = 'Screenshot' ))
94+
95+ logging .info ("Screenshot saved: %s" , screenshot_path )
96+ except Exception as exc : # pylint: disable=broad-exception-caught
97+ logging .error ("Failed to capture screenshot: %s" , str (exc ))
98+
5799 handler , stream = log_streams .get (item .nodeid , (None , None ))
58100
59101 if handler and stream :
@@ -65,8 +107,44 @@ def pytest_runtest_makereport(item, call):
65107 logger = logging .getLogger ()
66108 logger .removeHandler (handler )
67109
68- # Store the log output on the report object for HTML reporting
69- report .description = f"<pre>{ log_output .strip ()} </pre>"
110+ # Check if there are subtests
111+ subtests_html = ""
112+ if hasattr (item , 'user_properties' ):
113+ item_subtests = [
114+ prop [1 ] for prop in item .user_properties if prop [0 ] == "subtest"
115+ ]
116+ if item_subtests :
117+ subtests_html = (
118+ "<div style='margin-top: 10px;'>"
119+ "<strong>Step-by-Step Details:</strong>"
120+ "<ul style='list-style: none; padding-left: 0;'>"
121+ )
122+ for idx , subtest in enumerate (item_subtests , 1 ):
123+ status = "✅ PASSED" if subtest .get ('passed' ) else "❌ FAILED"
124+ status_color = "green" if subtest .get ('passed' ) else "red"
125+ subtests_html += (
126+ f"<li style='margin: 10px 0; padding: 10px; "
127+ f"border-left: 3px solid { status_color } ; "
128+ f"background-color: #f9f9f9;'>"
129+ )
130+ subtests_html += (
131+ f"<div style='font-weight: bold; color: { status_color } ;'>"
132+ f"{ status } - { subtest .get ('msg' , f'Step { idx } ' )} </div>"
133+ )
134+ if subtest .get ('logs' ):
135+ subtests_html += (
136+ f"<pre style='margin: 5px 0; padding: 5px; "
137+ f"background-color: #fff; border: 1px solid #ddd; "
138+ f"font-size: 11px;'>{ subtest .get ('logs' ).strip ()} </pre>"
139+ )
140+ subtests_html += "</li>"
141+ subtests_html += "</ul></div>"
142+
143+ # Combine main log output with subtests
144+ if subtests_html :
145+ report .description = f"<pre>{ log_output .strip ()} </pre>{ subtests_html } "
146+ else :
147+ report .description = f"<pre>{ log_output .strip ()} </pre>"
70148
71149 # Clean up references
72150 log_streams .pop (item .nodeid , None )
0 commit comments