#!/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())