@@ -32,6 +32,8 @@ class US05Tests(unittest.TestCase):
3232 FIRST_TIME_ALERT_BUTTON_SELECTOR = ".alert__button.alert__button--single"
3333 ALERT_OVERLAY_SELECTOR = "div.alert__overlay"
3434 NAV_BACK_BUTTON_XPATH = "//button[contains(@class, 'nav-bar__button') and .//span[contains(@class, 'back-icon')]/svg[contains(@class, 'lucide-chevron-left')]]"
35+ # Simpler CSS selector for the back button - targeting the first button in the nav-bar
36+ NAV_BACK_BUTTON_SELECTOR = "nav.nav-bar > button.nav-bar__button:first-child"
3537 HOME_CONTAINER_SELECTOR = "div.home__container"
3638 SETTINGS_NAV_BUTTON_XPATH = "//button[contains(@class, 'nav-bar__button') and .//span[contains(@class, 'settings-icon')]/svg[contains(@class, 'lucide-settings')]]"
3739 APPROVAL_GRADE_INPUT_SELECTOR = "input.settings__input[type='number']" # Hypothetical for settings page
@@ -65,14 +67,14 @@ def setUp(self):
6567 logger .error ("Driver is not initialized after setup attempt." )
6668 self .fail ("Driver could not be initialized." )
6769 return
68- self ._initial_setup ()
69-
70+ self ._initial_setup ()
71+
7072 def tearDown (self ):
7173 if hasattr (self , 'is_driver_managed_by_fallback' ) and self .is_driver_managed_by_fallback :
7274 if self .driver :
7375 self .driver .quit ()
7476 # For pytest-managed driver, teardown is handled by the fixture
75-
77+
7678 def _take_screenshot (self , name_suffix ):
7779 timestamp = int (time .time ())
7880 test_method_name = getattr (self , '_testMethodName' , 'unknown_test' )
@@ -83,21 +85,41 @@ def _take_screenshot(self, name_suffix):
8385 logger .info (f"Screenshot saved: { screenshot_name } " )
8486 except Exception as e :
8587 logger .error (f"Error saving screenshot { screenshot_name } : { e } " )
86-
88+
8789 def _initial_setup (self ):
90+ if not hasattr (self , 'driver' ) or not self .driver :
91+ logger .error ("Driver not initialized in _initial_setup. Aborting setup." )
92+ self .fail ("Driver not initialized for test setup." )
93+ return
94+
8895 self .driver .get (self .BASE_URL )
8996 logger .info (f"Navigated to base URL: { self .BASE_URL } " )
9097 try :
98+ logger .info (f"Attempting to handle first-time alert with button '{ self .FIRST_TIME_ALERT_BUTTON_SELECTOR } '." )
9199 alert_button = WebDriverWait (self .driver , 10 ).until (
92100 EC .element_to_be_clickable ((By .CSS_SELECTOR , self .FIRST_TIME_ALERT_BUTTON_SELECTOR ))
93101 )
94102 alert_button .click ()
103+ logger .info (f"Clicked first-time alert button: '{ self .FIRST_TIME_ALERT_BUTTON_SELECTOR } '." )
104+
95105 WebDriverWait (self .driver , 5 ).until (
96106 EC .invisibility_of_element_located ((By .CSS_SELECTOR , self .ALERT_OVERLAY_SELECTOR ))
97107 )
98- nav_back_button = WebDriverWait (self .driver , 10 ).until (
99- EC .element_to_be_clickable ((By .XPATH , self .NAV_BACK_BUTTON_XPATH ))
100- )
108+ logger .info (f"Alert overlay '{ self .ALERT_OVERLAY_SELECTOR } ' is no longer visible. App should be on Settings page." )
109+
110+ # Try CSS selector first, fall back to XPath if needed
111+ try :
112+ nav_back_button = WebDriverWait (self .driver , 5 ).until (
113+ EC .element_to_be_clickable ((By .CSS_SELECTOR , self .NAV_BACK_BUTTON_SELECTOR ))
114+ )
115+ logger .info ("Found back button using CSS selector." )
116+ except TimeoutException :
117+ logger .info ("CSS selector failed for back button, trying XPath..." )
118+ nav_back_button = WebDriverWait (self .driver , 5 ).until (
119+ EC .element_to_be_clickable ((By .XPATH , self .NAV_BACK_BUTTON_XPATH ))
120+ )
121+ logger .info ("Found back button using XPath selector." )
122+
101123 nav_back_button .click ()
102124 WebDriverWait (self .driver , 10 ).until (
103125 EC .presence_of_element_located ((By .CSS_SELECTOR , self .HOME_CONTAINER_SELECTOR ))
@@ -116,11 +138,9 @@ def _initial_setup(self):
116138 logger .error (f"An unexpected error occurred during initial setup: { e } " , exc_info = True )
117139 self ._take_screenshot ("initial_setup_error" )
118140 self .fail (f"Unexpected error during initial setup: { e } " )
119-
120- # Ensure approval grade is 3.0 (default)
141+ # Ensure approval grade is 3.0 (default)
121142 self ._set_approval_grade ("3.0" )
122143
123-
124144 def _set_approval_grade (self , approval_grade_value ):
125145 logger .info (f"Setting approval grade to: { approval_grade_value } " )
126146 # Navigate to settings if not already there or on home
@@ -155,9 +175,13 @@ def _set_approval_grade(self, approval_grade_value):
155175 self .driver .find_element (By .XPATH , self .NAV_BACK_BUTTON_XPATH ).click ()
156176 self .wait_long .until (EC .presence_of_element_located ((By .CSS_SELECTOR , self .HOME_CONTAINER_SELECTOR )))
157177 logger .info ("Navigated back to Home page from Settings." )
158-
159-
160178 def _add_grade_and_percentage (self , grade , percentage ):
179+ if not hasattr (self , 'driver' ) or not self .driver :
180+ logger .error ("Driver not available in _add_grade_and_percentage." )
181+ self .fail ("Driver not available." )
182+ return
183+
184+ # First check if we're on the result page and navigate back if needed
161185 on_result_page = False
162186 try :
163187 if self .driver .find_element (By .CSS_SELECTOR , self .RESULT_PAGE_CONTAINER_SELECTOR ).is_displayed ():
@@ -166,29 +190,124 @@ def _add_grade_and_percentage(self, grade, percentage):
166190 on_result_page = False
167191
168192 if on_result_page :
169- logger .info ("On result page, navigating back to home to add grades." )
170- self .driver .find_element (By .XPATH , self .NAV_BACK_BUTTON_XPATH ).click ()
193+ logger .info ("Currently on result page, navigating back to home to add grades." )
194+ try :
195+ # First try with the CSS selector
196+ nav_back_button = self .wait_short .until (
197+ EC .element_to_be_clickable ((By .CSS_SELECTOR , self .NAV_BACK_BUTTON_SELECTOR ))
198+ )
199+ logger .info ("Found back button using CSS selector in _add_grade_and_percentage." )
200+ except TimeoutException :
201+ try :
202+ # Then try with XPath
203+ logger .info ("CSS selector failed for back button in _add_grade_and_percentage, trying XPath..." )
204+ nav_back_button = self .wait_long .until (
205+ EC .element_to_be_clickable ((By .XPATH , self .NAV_BACK_BUTTON_XPATH ))
206+ )
207+ logger .info ("Found back button using XPath selector in _add_grade_and_percentage." )
208+ except TimeoutException :
209+ # As a last resort, try to find any buttons in the nav bar
210+ logger .info ("XPath also failed. Looking for any button in the nav bar..." )
211+ nav_bar = self .wait_long .until (
212+ EC .presence_of_element_located ((By .CSS_SELECTOR , "nav.nav-bar" ))
213+ )
214+ nav_buttons = nav_bar .find_elements (By .TAG_NAME , "button" )
215+ if nav_buttons :
216+ nav_back_button = nav_buttons [0 ] # First button in nav bar is usually back
217+ logger .info ("Using first button found in nav bar" )
218+ else :
219+ logger .error ("No buttons found in nav bar" )
220+ self ._take_screenshot ("no_buttons_in_nav_bar_add_grade" )
221+ self .fail ("No buttons found in nav bar when trying to navigate back" )
222+
223+ # Click the back button once found
224+ nav_back_button .click ()
171225 self .wait_long .until (EC .presence_of_element_located ((By .CSS_SELECTOR , self .HOME_CONTAINER_SELECTOR )))
226+ logger .info ("Successfully navigated back to home page." )
227+
228+ # Take a small pause to ensure the UI is fully loaded
229+ time .sleep (0.5 )
172230
231+ # Now we're on the home page
232+ # Take a screenshot of the current state for debugging
233+ self ._take_screenshot ("before_adding_grade" )
234+
235+ # Find all grade rows currently in the UI
173236 grade_rows = self .driver .find_elements (By .CSS_SELECTOR , self .GRADES_LIST_ITEM_SELECTOR )
174- if not grade_rows :
175- self .driver .find_element (By .CSS_SELECTOR , self .ADD_GRADE_BUTTON_SELECTOR ).click ()
176- time .sleep (0.5 )
237+ logger .info (f"Found { len (grade_rows )} grade rows before adding." )
238+
239+ # First check if we need to add a new row
240+ need_new_row = True
241+ if grade_rows :
242+ # Check if the last row is empty (which means we can use it)
243+ last_row = grade_rows [- 1 ]
244+ try :
245+ grade_input = last_row .find_element (By .CSS_SELECTOR , self .GRADE_INPUT_SELECTOR )
246+ percentage_input = last_row .find_element (By .CSS_SELECTOR , self .PERCENTAGE_INPUT_SELECTOR )
247+
248+ grade_value = grade_input .get_attribute ("value" )
249+ percentage_value = percentage_input .get_attribute ("value" )
250+
251+ # If both inputs are empty, we can use this row
252+ if not grade_value and not percentage_value :
253+ need_new_row = False
254+ logger .info ("Found empty row to use for new grade entry." )
255+ except NoSuchElementException :
256+ # If we can't find inputs in the last row, we'll add a new one
257+ logger .warning ("Last row doesn't have expected input elements." )
258+ need_new_row = True
259+
260+ # If we need a new row or there are no rows, add one
261+ if need_new_row or not grade_rows :
262+ logger .info ("Adding a new grade row..." )
263+ add_button = self .wait_long .until (EC .element_to_be_clickable ((By .CSS_SELECTOR , self .ADD_GRADE_BUTTON_SELECTOR )))
264+ add_button .click ()
265+ time .sleep (0.5 ) # Wait for the row to be added
266+
267+ # Refresh the list of grade rows
177268 grade_rows = self .driver .find_elements (By .CSS_SELECTOR , self .GRADES_LIST_ITEM_SELECTOR )
178- if not grade_rows : self .fail ("Failed to add initial grade row." )
179-
180- last_row = grade_rows [- 1 ]
181- grade_input_element = last_row .find_element (By .CSS_SELECTOR , self .GRADE_INPUT_SELECTOR )
182- percentage_input_element = last_row .find_element (By .CSS_SELECTOR , self .PERCENTAGE_INPUT_SELECTOR )
269+ if not grade_rows :
270+ logger .error ("Failed to add grade row - no rows found after clicking 'Add' button" )
271+ self ._take_screenshot ("failed_to_add_grade_row" )
272+ self .fail ("Failed to add grade row - no rows found after clicking 'Add' button" )
183273
184- grade_input_element .clear ()
185- grade_input_element .send_keys (str (grade ))
186- percentage_input_element .clear ()
187- percentage_input_element .send_keys (str (percentage ))
274+ # Now use the last row to add our grade
275+ last_row = grade_rows [- 1 ]
276+ logger .info (f"Using the last of { len (grade_rows )} rows to add grade: { grade } , percentage: { percentage } " )
188277
189- self .driver .find_element (By .CSS_SELECTOR , self .ADD_GRADE_BUTTON_SELECTOR ).click ()
190- time .sleep (0.5 )
191- logger .info (f"Added grade: { grade } , percentage: { percentage } ." )
278+ try :
279+ grade_input_element = last_row .find_element (By .CSS_SELECTOR , self .GRADE_INPUT_SELECTOR )
280+ percentage_input_element = last_row .find_element (By .CSS_SELECTOR , self .PERCENTAGE_INPUT_SELECTOR )
281+
282+ # Clear and enter the grade and percentage
283+ grade_input_element .clear ()
284+ grade_input_element .send_keys (str (grade ))
285+ percentage_input_element .clear ()
286+ percentage_input_element .send_keys (str (percentage ))
287+
288+ # Take screenshot after entering values
289+ self ._take_screenshot (f"after_entering_grade_{ grade } _percent_{ percentage } " )
290+
291+ # Click the add button to confirm this grade
292+ add_button = self .wait_long .until (EC .element_to_be_clickable ((By .CSS_SELECTOR , self .ADD_GRADE_BUTTON_SELECTOR )))
293+ add_button .click ()
294+ logger .info (f"Clicked 'Add' button after entering grade: { grade } , percentage: { percentage } " )
295+
296+ # Allow time for the UI to update
297+ time .sleep (0.5 )
298+
299+ # Verify the grade was added by checking for more rows or for values in the inputs
300+ new_grade_rows = self .driver .find_elements (By .CSS_SELECTOR , self .GRADES_LIST_ITEM_SELECTOR )
301+ logger .info (f"After adding: { len (new_grade_rows )} grade rows (previously { len (grade_rows )} )" )
302+
303+ # Take screenshot after adding grade
304+ self ._take_screenshot ("after_adding_grade" )
305+
306+ except NoSuchElementException as e :
307+ logger .error (f"Could not find input elements in the grade row: { e } " )
308+ self ._take_screenshot ("input_elements_not_found" )
309+ self .fail (f"Input elements not found: { e } " )
310+ return
192311
193312 def _get_required_grade_or_message (self ):
194313 raw_text = ""
0 commit comments