Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 28 additions & 21 deletions qa_agent/testers/accessibility.py
Original file line number Diff line number Diff line change
@@ -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."""
Expand Down Expand Up @@ -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:
Expand All @@ -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)."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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']
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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:
Expand All @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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)
60 changes: 36 additions & 24 deletions qa_agent/testers/forms.py
Original file line number Diff line number Diff line change
@@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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:
Expand All @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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:
Expand All @@ -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."""
Expand Down Expand Up @@ -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:
Expand All @@ -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."""
Expand Down Expand Up @@ -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)
34 changes: 20 additions & 14 deletions qa_agent/testers/keyboard.py
Original file line number Diff line number Diff line change
@@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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)
Loading
Loading