@@ -17,7 +17,10 @@ class US03Tests(unittest.TestCase):
1717 PERCENTAGE_INPUT_SELECTOR = "input.home__input[placeholder='0'][type='number']"
1818 ADD_GRADE_BUTTON_SELECTOR = "button.home__add-button"
1919 GRADES_LIST_ITEM_SELECTOR = "div.home__grades-container > div.home__grade-row"
20- REMAINING_PERCENTAGE_DISPLAY_SELECTOR = "div.home__results-card p.home__card-text span"
20+ CALCULATE_BUTTON_SELECTOR = "button.home__calculate-button" # Added
21+ ALERT_TITLE_SELECTOR = "div.alert__title" # Added
22+ ALERT_MESSAGE_SELECTOR = "div.alert__message" # Added
23+ ALERT_CONFIRM_BUTTON_SELECTOR = "div.alert__actions button.alert__button" # General confirm, can be more specific if needed
2124
2225 FIRST_TIME_ALERT_BUTTON_SELECTOR = ".alert__button.alert__button--single"
2326 ALERT_OVERLAY_SELECTOR = "div.alert__overlay"
@@ -126,16 +129,38 @@ def _add_grade_and_percentage(self, grade, percentage):
126129 self .fail ("Driver not available." )
127130 return
128131
132+ # Find all grade input rows. The last one is the one we want to fill.
133+ # If it's the first grade, it's the only row.
134+ # If we've added one, a new empty row is typically added by the app, so we target that.
135+
136+ # Click "Agregar nota" first to ensure a new empty row is available if needed,
137+ # or to use the existing one if it's the first entry.
138+ # This logic might need adjustment based on how `handleAddGrade` in home.jsx works.
139+ # For now, assume we always fill the *last* available input row.
140+
141+ # If it's not the first grade being added in the test sequence, click "Agregar nota"
142+ # to generate a new row for this entry.
143+ # This assumes that _add_grade_and_percentage is called for each new grade entry
144+ # and the app adds a new row template upon clicking "Agregar nota" *after* filling the previous.
145+
146+ # Let's refine: The "Agregar nota" button adds a new *blank* row.
147+ # We should fill the *current last* row's inputs, then click "Agregar nota"
148+ # if we intend to add *another* one after this.
149+ # The current _add_grade_and_percentage is designed to fill inputs and then click "Agregar nota".
150+ # This implies "Agregar nota" serves dual purpose: commit current and prepare next.
151+ # This seems to be the behavior from US01/US02.
152+
129153 grade_rows = self .driver .find_elements (By .CSS_SELECTOR , self .GRADES_LIST_ITEM_SELECTOR )
130154 if not grade_rows :
131155 logger .error ("No grade rows found to add grade and percentage." )
132156 self ._take_screenshot ("no_grade_rows_found" )
133157 self .fail ("No grade rows found." )
134158 return
135159
136- last_row = grade_rows [- 1 ]
160+ last_row = grade_rows [- 1 ] # Target the last row for input
137161 logger .info (f"Targeting the last of { len (grade_rows )} grade rows for input." )
138162
163+
139164 try :
140165 grade_input_element = last_row .find_element (By .CSS_SELECTOR , self .GRADE_INPUT_SELECTOR )
141166 percentage_input_element = last_row .find_element (By .CSS_SELECTOR , self .PERCENTAGE_INPUT_SELECTOR )
@@ -146,109 +171,92 @@ def _add_grade_and_percentage(self, grade, percentage):
146171 self .fail ("Input elements not found in the last row." )
147172 return
148173
174+ # The "Agregar nota" button is outside the rows, so it's found globally.
149175 add_button = self .wait_long .until (EC .element_to_be_clickable ((By .CSS_SELECTOR , self .ADD_GRADE_BUTTON_SELECTOR )))
150176
151177 grade_input_element .clear ()
152178 grade_input_element .send_keys (str (grade ))
153179 percentage_input_element .clear ()
154180 percentage_input_element .send_keys (str (percentage ))
155181
182+ # Click "Agregar nota" AFTER filling the inputs.
183+ # This should add the current grade and potentially prepare a new row.
156184 add_button .click ()
157- logger .info (f"Clicked 'Agregar nota' after attempting to add grade: { grade } , percentage: { percentage } to the last row." )
158- # Wait for UI to update, especially if new rows are added dynamically.
185+ logger .info (f"Clicked \' Agregar nota\' after filling grade: { grade } , percentage: { percentage } into the last available row." )
186+
187+ # Wait for UI to update, e.g., for a new row to be added if that's the behavior.
159188 # A more robust wait would be for the number of rows to change or for a specific element to appear.
160189 time .sleep (0.5 ) # Small delay for React state updates
161190
162- def _get_remaining_percentage (self ):
163- cleaned_text = "" # Initialize cleaned_text
164- try :
165- # Wait for the element to be present and contain some text
166- percentage_element = self .wait_long .until (
167- EC .presence_of_element_located ((By .CSS_SELECTOR , self .REMAINING_PERCENTAGE_DISPLAY_SELECTOR ))
168- )
169- # Add a small delay or check for text to be non-empty if necessary
170- time .sleep (0.2 ) # give a brief moment for text to update
171-
172- # Attempt to get text, retry if it's empty initially due to rendering delays
173- text_value = percentage_element .text .strip ()
174- attempts = 0
175- while text_value == "" and attempts < 5 : # Retry if empty
176- time .sleep (0.3 )
177- text_value = percentage_element .text .strip ()
178- attempts += 1
179-
180- logger .info (f"Raw remaining percentage text: '{ text_value } '" )
181- if not text_value : # If still empty after retries
182- logger .warning ("Remaining percentage text is empty after retries." )
183- self ._take_screenshot ("empty_remaining_percentage" )
184- return "Error: Empty Value" # Or raise an error
185-
186- # Assuming the text is like "XX %" or "XX%", we remove "%" and convert to float
187- # If it's just "XX", this will also work.
188- cleaned_text = text_value .replace ('%' , '' ).strip ()
189- return float (cleaned_text )
190- except TimeoutException :
191- logger .error (f"Timeout waiting for remaining percentage display element: { self .REMAINING_PERCENTAGE_DISPLAY_SELECTOR } " )
192- self ._take_screenshot ("remaining_percentage_timeout" )
193- return "Error: Timeout" # Or raise an error
194- except ValueError :
195- logger .error (f"Could not convert remaining percentage text '{ cleaned_text } ' to float." ) # cleaned_text is now defined
196- self ._take_screenshot ("remaining_percentage_value_error" )
197- return "Error: Conversion" # Or raise an error
198- except Exception as e :
199- logger .error (f"Error getting remaining percentage: { e } " )
200- self ._take_screenshot ("get_remaining_percentage_error" )
201- return "Error: General" # Or raise an error
202-
203- # US03: Calculo del porcentaje faltante
204- def test_us03_verify_calculation_of_remaining_percentage (self ):
191+ # US03: Verify alert for percentage sum > 100%
192+ def test_us03_verify_percentage_sum_alert (self ):
205193 test_name = self ._testMethodName
206194 logger .info (f"Running test: { test_name } " )
207195
208196 try :
209- # 1. Initial state: Verify remaining percentage is 100%
210- logger .info ("Verifying initial remaining percentage." )
211- initial_remaining = self ._get_remaining_percentage ()
212- self .assertEqual (initial_remaining , 100.0 ,
213- f"Initial remaining percentage expected 100.0, but got { initial_remaining } " )
214- logger .info (f"Initial remaining percentage is correct: { initial_remaining } %" )
197+ # 1. Add grades with percentages summing > 100%
198+ logger .info ("Adding first grade (70%)." )
199+ self ._add_grade_and_percentage ("3.0" , "70" )
200+
201+ # The previous _add_grade_and_percentage clicks "Agregar nota",
202+ # which should make a new row available for the next grade.
203+ logger .info ("Adding second grade (40%). Total percentage should be 110%." )
204+ self ._add_grade_and_percentage ("4.0" , "40" )
205+ # After this, we should have two rows with data, and possibly a third empty template row.
206+
207+ # 2. Click "Calcular" button
208+ logger .info (f"Clicking \' Calcular\' button with selector: { self .CALCULATE_BUTTON_SELECTOR } " )
209+ calculate_button = self .wait_long .until (
210+ EC .element_to_be_clickable ((By .CSS_SELECTOR , self .CALCULATE_BUTTON_SELECTOR ))
211+ )
212+ calculate_button .click ()
213+ logger .info ("\' Calcular\' button clicked." )
214+
215+ # 3. Verify the alert is displayed with correct title and message
216+ logger .info ("Verifying alert for percentage sum > 100%." )
217+
218+ alert_title_element = self .wait_long .until (
219+ EC .visibility_of_element_located ((By .CSS_SELECTOR , self .ALERT_TITLE_SELECTOR ))
220+ )
221+ alert_title_text = alert_title_element .text
222+ expected_title = "La suma de los porcentajes es mayor al 100%"
223+ self .assertEqual (alert_title_text , expected_title ,
224+ f"Alert title expected \' { expected_title } \' , but got \' { alert_title_text } \' " )
225+ logger .info (f"Alert title is correct: \' { alert_title_text } \' " )
215226
216- # 2. Add one grade and verify
217- logger .info ("Adding first grade (20%)." )
218- self ._add_grade_and_percentage ("3.0" , "20" )
219- # It might take a moment for the calculation to update
220- time .sleep (0.5 ) # Wait for calculation update
221- remaining_after_first_grade = self ._get_remaining_percentage ()
222- self .assertEqual (remaining_after_first_grade , 80.0 ,
223- f"Remaining percentage after 1st grade (20%) expected 80.0, but got { remaining_after_first_grade } " )
224- logger .info (f"Remaining percentage after 1st grade (20%) is correct: { remaining_after_first_grade } %" )
227+ alert_message_element = self .wait_long .until (
228+ EC .visibility_of_element_located ((By .CSS_SELECTOR , self .ALERT_MESSAGE_SELECTOR ))
229+ )
230+ alert_message_text = alert_message_element .text
231+ expected_message = "Por favor verifica los porcentajes de las notas ingresadas, para que la suma sea menor o igual a 100%."
232+ self .assertEqual (alert_message_text , expected_message ,
233+ f"Alert message expected \' { expected_message } \' , but got \' { alert_message_text } \' " )
234+ logger .info (f"Alert message is correct: \' { alert_message_text } \' " )
225235
226- # 3. Add a second grade and verify
227- logger .info ("Adding second grade (30%). Total 50%." )
228- self ._add_grade_and_percentage ("4.0" , "30" )
229- time .sleep (0.5 ) # Wait for calculation update
230- remaining_after_second_grade = self ._get_remaining_percentage ()
231- self .assertEqual (remaining_after_second_grade , 50.0 ,
232- f"Remaining percentage after 2nd grade (total 50%) expected 50.0, but got { remaining_after_second_grade } " )
233- logger .info (f"Remaining percentage after 2nd grade (total 50%) is correct: { remaining_after_second_grade } %" )
236+ # 4. Close the alert
237+ alert_confirm_button = self .wait_long .until (
238+ EC .element_to_be_clickable ((By .CSS_SELECTOR , self .ALERT_CONFIRM_BUTTON_SELECTOR ))
239+ )
240+ # Based on alert.jsx, if showCancel is false (which it is for this alert),
241+ # the confirm button will have 'alert__button--single'
242+ # We can use a more general selector for the button within alert__actions
243+ alert_confirm_button .click ()
244+ logger .info ("Alert confirm button clicked." )
234245
235- # 4. Add a third grade to make total 100% and verify
236- logger .info ("Adding third grade (50%). Total 100%." )
237- self ._add_grade_and_percentage ("5.0" , "50" )
238- time .sleep (0.5 ) # Wait for calculation update
239- remaining_after_third_grade = self ._get_remaining_percentage ()
240- self .assertEqual (remaining_after_third_grade , 0.0 ,
241- f"Remaining percentage after 3rd grade (total 100%) expected 0.0, but got { remaining_after_third_grade } " )
242- logger .info (f"Remaining percentage after 3rd grade (total 100%) is correct: { remaining_after_third_grade } %" )
246+ # Verify alert is closed
247+ WebDriverWait (self .driver , 5 ).until (
248+ EC .invisibility_of_element_located ((By .CSS_SELECTOR , self .ALERT_OVERLAY_SELECTOR ))
249+ )
250+ logger .info ("Alert successfully closed." )
243251
244252 except AssertionError as e :
245253 logger .error (f"AssertionError in { test_name } : { e } " )
246254 self ._take_screenshot (f"{ test_name } _assertion_failure" )
247- raise # Re-raise the assertion error to fail the test
255+ raise
248256 except Exception as e :
249257 logger .error (f"An unexpected error occurred in { test_name } : { e } " )
250258 self ._take_screenshot (f"{ test_name } _unexpected_error" )
251- raise # Re-raise to fail the test
259+ raise
252260
253261 logger .info (f"Test { test_name } completed successfully." )
254262
0 commit comments