diff --git a/tests/test_a11y_r1_password_toggle.py b/tests/test_a11y_r1_password_toggle.py new file mode 100644 index 00000000..901cae0b --- /dev/null +++ b/tests/test_a11y_r1_password_toggle.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +""" +Smoke test: a11y R1 password visibility toggle exists and is correctly structured. +Issue #551: [a11y] R1: Add password visibility toggle on login + +Verifies that deploy/gitea-a11y/custom/templates/user/auth/signin_inner.tmpl +contains all required elements for an accessible password toggle: +- password input with type="password" and proper id/name +- toggle button with id="toggle-password", aria-label, title, and onclick +- JavaScript function togglePasswordVisibility() that toggles type and aria-label +""" + +import sys +from pathlib import Path + +# Resolve repo root +REPO_ROOT = Path(__file__).resolve().parent.parent +TEMPLATE_PATH = REPO_ROOT / "deploy" / "gitea-a11y" / "custom" / "templates" / "user" / "auth" / "signin_inner.tmpl" + + +def test_r1_toggle_button_exists(): + """Template contains the toggle button with required attributes.""" + content = TEMPLATE_PATH.read_text() + + # Check button exists with proper ID + assert 'id="toggle-password"' in content, "Toggle button missing id='toggle-password'" + + # Check button has type="button" + assert 'type="button"' in content, "Toggle button missing type='button'" + + # Check aria-label (initially "Show password") + assert 'aria-label="Show password"' in content, "Toggle button missing initial aria-label='Show password'" + + # Check title attribute + assert 'title="Show password"' in content, "Toggle button missing initial title='Show password'" + + # Check onclick calls togglePasswordVisibility() + assert 'onclick="togglePasswordVisibility()"' in content, "Toggle button missing onclick handler" + + print("āœ“ Toggle button exists with required attributes") + + +def test_r1_password_input_exists(): + """Template contains password input with proper id and type.""" + content = TEMPLATE_PATH.read_text() + + # Check password input id and name + assert 'id="password"' in content, "Password input missing id='password'" + assert 'name="password"' in content, "Password input missing name='password'" + + # Check type is "password" + assert 'type="password"' in content, "Password input missing type='password'" + + # Check required and aria-required for a11y + assert 'required' in content, "Password input missing required attribute" + assert 'aria-required="true"' in content, "Password input missing aria-required='true'" + + print("āœ“ Password input exists with type='password' and required a11y") + + +def test_r1_javascript_function_exists(): + """Template contains togglePasswordVisibility JS function.""" + content = TEMPLATE_PATH.read_text() + + # Function definition + assert 'function togglePasswordVisibility()' in content, "Missing togglePasswordVisibility function" + + # Function toggles input.type + assert "input.type = 'text'" in content, "Function doesn't set input.type to 'text'" + assert "input.type = 'password'" in content, "Function doesn't reset input.type to 'password'" + + # Function updates aria-label + assert "'Hide password'" in content, "Function doesn't update aria-label to 'Hide password'" + assert "'Show password'" in content, "Function doesn't update aria-label back to 'Show password'" + + # Function updates button textContent (emoji) + assert "btn.textContent = 'šŸ™ˆ'" in content, "Function missing hide emoji" + assert "btn.textContent = 'šŸ‘'" in content, "Function missing show emoji" + + print("āœ“ JavaScript toggle function correctly implemented") + + +def test_r1_label_association(): + """Password field has associated label.""" + content = TEMPLATE_PATH.read_text() + + # Label for attribute matches input id + assert 'for="password"' in content, "Label missing for='password'" + assert 'id="password"' in content, "Input missing id='password'" + + print("āœ“ Label correctly associated with password input") + + +def run_all(): + """Run all R1 checks.""" + print("R1 Smoke Test — Password Visibility Toggle (issue #551)") + print("-" * 60) + try: + test_r1_toggle_button_exists() + test_r1_password_input_exists() + test_r1_javascript_function_exists() + test_r1_label_association() + print("-" * 60) + print("āœ… All R1 checks passed") + return 0 + except AssertionError as e: + print(f"\nāŒ R1 check failed: {e}") + return 1 + except Exception as e: + print(f"\nāŒ Unexpected error: {e}") + return 1 + + +if __name__ == "__main__": + sys.exit(run_all())