From 6d19323df693a7b92b8a07c17c22f8ce0521e6b3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 11:50:56 +0000 Subject: [PATCH] fix: replace silent exception swallowing with debug logging in tester classes All bare `except Exception: pass` and `except Exception: continue` blocks in AccessibilityTester, FormTester, KeyboardTester, and MouseTester now capture the exception and emit a logger.debug() message (matching the WCAGComplianceTester pattern). Outer method-level handlers log which method failed; inner per-element loop handlers log which element step failed before continuing. This gives visibility into failures without breaking the overall test run. https://claude.ai/code/session_01NcTcxLT21Zg123dBFd8fx9 --- qa_agent/testers/accessibility.py | 49 ++++++++++++++----------- qa_agent/testers/forms.py | 60 ++++++++++++++++++------------- qa_agent/testers/keyboard.py | 34 ++++++++++-------- qa_agent/testers/mouse.py | 49 ++++++++++++++----------- 4 files changed, 113 insertions(+), 79 deletions(-) diff --git a/qa_agent/testers/accessibility.py b/qa_agent/testers/accessibility.py index 1182348..50dc64b 100644 --- a/qa_agent/testers/accessibility.py +++ b/qa_agent/testers/accessibility.py @@ -1,11 +1,15 @@ """Accessibility testing module.""" +import logging + from playwright.sync_api import Page from ..config import TestConfig from ..models import Finding, FindingCategory, Severity from .base import BaseTester +logger = logging.getLogger(__name__) + class AccessibilityTester(BaseTester): """Tests accessibility issues and WCAG compliance.""" @@ -78,7 +82,8 @@ def _test_images_alt_text(self): if info['isInLink'] and info['alt'] in ['click here', 'read more', 'link', 'image']: issues['suspicious_alt'].append({"alt": info['alt'], "src": info['src'], "issue": "link_image"}) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating image element: %s", self.__class__.__name__, e) continue if len(issues['missing_alt']) > 0: @@ -105,8 +110,8 @@ def _test_images_alt_text(self): metadata={"images": issues['suspicious_alt'][:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_images_alt_text failed: %s", self.__class__.__name__, e) def _test_headings_structure(self): """Test heading hierarchy (h1-h6).""" @@ -171,8 +176,8 @@ def _test_headings_structure(self): metadata={"skips": skipped_levels[:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_headings_structure failed: %s", self.__class__.__name__, e) def _test_link_text(self): """Test that links have descriptive text.""" @@ -220,7 +225,8 @@ def _test_link_text(self): "href": info['href'] }) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating link element: %s", self.__class__.__name__, e) continue empty_links = [lnk for lnk in bad_links if lnk['issue'] == 'empty'] @@ -250,8 +256,8 @@ def _test_link_text(self): metadata={"links": generic_links[:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_link_text failed: %s", self.__class__.__name__, e) def _test_color_contrast(self): """Test color contrast of text elements.""" @@ -315,7 +321,8 @@ def _test_color_contrast(self): if contrast_info and not contrast_info.get('passes') and not contrast_info.get('isTransparentBg'): low_contrast.append(contrast_info) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating contrast for element: %s", self.__class__.__name__, e) continue if len(low_contrast) > 3: @@ -330,8 +337,8 @@ def _test_color_contrast(self): metadata={"elements": low_contrast[:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_color_contrast failed: %s", self.__class__.__name__, e) def _test_aria_usage(self): """Test correct usage of ARIA attributes.""" @@ -439,8 +446,8 @@ def _test_aria_usage(self): actual_behavior=f"Referenced ID '{issue['id']}' not found", )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_aria_usage failed: %s", self.__class__.__name__, e) def _test_landmark_regions(self): """Test for proper landmark regions.""" @@ -477,8 +484,8 @@ def _test_landmark_regions(self): actual_behavior=f"Found {landmarks['main']} main landmarks", )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_landmark_regions failed: %s", self.__class__.__name__, e) def _test_language_attribute(self): """Test for lang attribute on html element.""" @@ -510,8 +517,8 @@ def _test_language_attribute(self): actual_behavior=f"Lang attribute value: '{lang_info.get('lang')}'", )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_language_attribute failed: %s", self.__class__.__name__, e) def _test_skip_links(self): """Test for skip navigation links.""" @@ -579,8 +586,8 @@ def _test_skip_links(self): actual_behavior="Skip link target ID not found", )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_skip_links failed: %s", self.__class__.__name__, e) def _test_motion_preferences(self): """Test respect for reduced motion preference.""" @@ -640,5 +647,5 @@ def _test_motion_preferences(self): actual_behavior="No prefers-reduced-motion media query found in stylesheets", )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_motion_preferences failed: %s", self.__class__.__name__, e) diff --git a/qa_agent/testers/forms.py b/qa_agent/testers/forms.py index 67e74b9..10a8f82 100644 --- a/qa_agent/testers/forms.py +++ b/qa_agent/testers/forms.py @@ -1,11 +1,15 @@ """Form testing module.""" +import logging + from playwright.sync_api import Page from ..config import TestConfig from ..models import Finding, FindingCategory, Severity from .base import BaseTester +logger = logging.getLogger(__name__) + class FormTester(BaseTester): """Tests form interactions, validation, and error handling.""" @@ -67,11 +71,12 @@ def _analyze_forms(self): self.forms_data.append(form_data) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating form element: %s", self.__class__.__name__, e) continue - except Exception: - pass + except Exception as e: + logger.debug("%s: _analyze_forms failed: %s", self.__class__.__name__, e) def _test_required_fields(self): """Test that required fields are properly indicated.""" @@ -106,7 +111,8 @@ def _test_required_fields(self): if not info['hasVisualIndicator'] and not info['hasAriaRequired']: unmarked_required.append(info) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating required field: %s", self.__class__.__name__, e) continue if len(unmarked_required) > 0: @@ -121,8 +127,8 @@ def _test_required_fields(self): metadata={"fields": unmarked_required[:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_required_fields failed: %s", self.__class__.__name__, e) def _test_input_validation(self): """Test input validation with various invalid inputs.""" @@ -183,11 +189,12 @@ def _test_input_validation(self): # Clear for next test element.clear() - except Exception: + except Exception as e: + logger.debug("%s: error testing input validation for %s: %s", self.__class__.__name__, test["field_type"], e) continue - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_input_validation failed: %s", self.__class__.__name__, e) def _test_error_messages(self): """Test that error messages are accessible and clear.""" @@ -235,11 +242,12 @@ def _test_error_messages(self): if not info.get('ariaLive') and info.get('role') != 'alert': pass # Only flag if we know it's dynamic - except Exception: + except Exception as e: + logger.debug("%s: error evaluating error message element: %s", self.__class__.__name__, e) continue - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_error_messages failed: %s", self.__class__.__name__, e) def _test_form_labels(self): """Test that form inputs have proper labels.""" @@ -303,7 +311,8 @@ def _test_form_labels(self): "issue": "no_label" }) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating input label: %s", self.__class__.__name__, e) continue # Report placeholder-only issues @@ -334,8 +343,8 @@ def _test_form_labels(self): metadata={"inputs": no_label[:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_form_labels failed: %s", self.__class__.__name__, e) def _test_input_types(self): """Test that inputs use appropriate HTML5 types.""" @@ -378,7 +387,8 @@ def _test_input_types(self): if info['suggestedType'] and info['suggestedType'] != info['currentType']: wrong_types.append(info) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating input type: %s", self.__class__.__name__, e) continue if len(wrong_types) > 0: @@ -393,8 +403,8 @@ def _test_input_types(self): metadata={"suggestions": wrong_types[:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_input_types failed: %s", self.__class__.__name__, e) def _test_autocomplete(self): """Test autocomplete attributes on form fields.""" @@ -426,7 +436,8 @@ def _test_autocomplete(self): if not info['hasAutocomplete'] or info['autocomplete'] == '': missing_autocomplete.append(info) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating autocomplete field: %s", self.__class__.__name__, e) continue if len(missing_autocomplete) > 2: @@ -441,8 +452,8 @@ def _test_autocomplete(self): metadata={"fields": missing_autocomplete[:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_autocomplete failed: %s", self.__class__.__name__, e) def _test_form_submission(self): """Test form submission behavior without actually submitting.""" @@ -498,8 +509,9 @@ def _test_form_submission(self): actual_behavior="No submit handler detected", )) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating form submission: %s", self.__class__.__name__, e) continue - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_form_submission failed: %s", self.__class__.__name__, e) diff --git a/qa_agent/testers/keyboard.py b/qa_agent/testers/keyboard.py index d277a4a..94a0978 100644 --- a/qa_agent/testers/keyboard.py +++ b/qa_agent/testers/keyboard.py @@ -1,11 +1,15 @@ """Keyboard navigation and input testing.""" +import logging + from playwright.sync_api import Page from ..config import TestConfig from ..models import Finding, FindingCategory, Severity from .base import BaseTester +logger = logging.getLogger(__name__) + class KeyboardTester(BaseTester): """Tests keyboard navigation and interactions.""" @@ -203,11 +207,12 @@ def _test_arrow_key_navigation(self): actual_behavior="Arrow key press had no effect", )) - except Exception: + except Exception as e: + logger.debug("%s: error testing arrow key on %s: %s", self.__class__.__name__, selector, e) continue - except Exception: - pass # Arrow key test is supplementary, don't fail on errors + except Exception as e: + logger.debug("%s: _test_arrow_key_navigation failed: %s", self.__class__.__name__, e) def _test_enter_activation(self): """Test that Enter key activates focused elements.""" @@ -243,11 +248,12 @@ def _test_enter_activation(self): actual_behavior="Element could not be focused via JavaScript", )) - except Exception: + except Exception as e: + logger.debug("%s: error testing enter activation on element: %s", self.__class__.__name__, e) continue - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_enter_activation failed: %s", self.__class__.__name__, e) def _test_escape_key(self): """Test that Escape key closes modals/dropdowns.""" @@ -277,8 +283,8 @@ def _test_escape_key(self): actual_behavior="Modal remained open after Escape key press", )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_escape_key failed: %s", self.__class__.__name__, e) def _test_keyboard_traps(self): """Test for keyboard traps where user cannot TAB out.""" @@ -336,8 +342,8 @@ def _test_keyboard_traps(self): visited_indices.add(focused_index) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_keyboard_traps failed: %s", self.__class__.__name__, e) def _test_focus_visibility(self): """Test that focus indicators are visible.""" @@ -387,8 +393,8 @@ def _test_focus_visibility(self): metadata={"elements": elements_without_focus_style[:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_focus_visibility failed: %s", self.__class__.__name__, e) def _test_shortcut_keys(self): """Test common keyboard shortcuts don't break the page.""" @@ -421,5 +427,5 @@ def _test_shortcut_keys(self): # Press Escape to close any dialogs that opened self.page.keyboard.press("Escape") - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_shortcut_keys failed: %s", self.__class__.__name__, e) diff --git a/qa_agent/testers/mouse.py b/qa_agent/testers/mouse.py index dc511d9..73eb812 100644 --- a/qa_agent/testers/mouse.py +++ b/qa_agent/testers/mouse.py @@ -1,11 +1,15 @@ """Mouse interaction testing.""" +import logging + from playwright.sync_api import Page from ..config import TestConfig from ..models import Finding, FindingCategory, Severity from .base import BaseTester +logger = logging.getLogger(__name__) + class MouseTester(BaseTester): """Tests mouse interactions and hover states.""" @@ -94,11 +98,12 @@ def _test_clickable_elements(self): actual_behavior=f"Disabled element has opacity {styles['opacity']}", )) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating clickable element %s: %s", self.__class__.__name__, selector, e) continue - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_clickable_elements failed: %s", self.__class__.__name__, e) def _test_hover_states(self): """Test hover states on interactive elements.""" @@ -148,7 +153,8 @@ def _test_hover_states(self): if not style_changed: elements_without_hover.append(before_styles.get('text', 'unknown')) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating hover state: %s", self.__class__.__name__, e) continue if len(elements_without_hover) > count * 0.5 and count > 3: @@ -163,8 +169,8 @@ def _test_hover_states(self): metadata={"elements": elements_without_hover[:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_hover_states failed: %s", self.__class__.__name__, e) def _test_double_click(self): """Test double-click behavior on elements.""" @@ -196,11 +202,12 @@ def _test_double_click(self): expected_behavior="Double-click handler should produce visible effect", actual_behavior="No visible change after double-click", )) - except Exception: + except Exception as e: + logger.debug("%s: error testing double-click handler: %s", self.__class__.__name__, e) continue - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_double_click failed: %s", self.__class__.__name__, e) def _test_right_click(self): """Test right-click/context menu behavior.""" @@ -244,8 +251,7 @@ def _test_right_click(self): self.page.keyboard.press("Escape") except Exception as e: - import logging - logging.getLogger(__name__).debug("_test_right_click failed: %s", e) + logger.debug("%s: _test_right_click failed: %s", self.__class__.__name__, e) def _test_drag_and_drop_targets(self): """Test drag-and-drop elements.""" @@ -276,11 +282,12 @@ def _test_drag_and_drop_targets(self): expected_behavior="Draggable elements should have aria-grabbed and proper role", actual_behavior="Missing ARIA attributes for drag-and-drop accessibility", )) - except Exception: + except Exception as e: + logger.debug("%s: error evaluating draggable element ARIA: %s", self.__class__.__name__, e) continue - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_drag_and_drop_targets failed: %s", self.__class__.__name__, e) def _test_click_target_sizes(self): """Test that click targets meet minimum size requirements.""" @@ -334,7 +341,8 @@ def _test_click_target_sizes(self): "size": f"{size['width']:.0f}x{size['height']:.0f}px" }) - except Exception: + except Exception as e: + logger.debug("%s: error measuring click target size: %s", self.__class__.__name__, e) continue if len(small_targets) > 3: @@ -349,8 +357,8 @@ def _test_click_target_sizes(self): metadata={"small_targets": small_targets[:10]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_click_target_sizes failed: %s", self.__class__.__name__, e) def _test_overlapping_elements(self): """Test for overlapping clickable elements.""" @@ -372,7 +380,8 @@ def _test_overlapping_elements(self): }; }""") elements_data.append(rect) - except Exception: + except Exception as e: + logger.debug("%s: error measuring element overlap rect: %s", self.__class__.__name__, e) continue # Check for overlaps @@ -396,5 +405,5 @@ def _test_overlapping_elements(self): metadata={"overlaps": overlaps[:5]}, )) - except Exception: - pass + except Exception as e: + logger.debug("%s: _test_overlapping_elements failed: %s", self.__class__.__name__, e)