"""Front-end controls: every form, button, and link that is rendered exists and behaves correctly for each role. Uses stdlib-only HTML parsing (html.parser). """ import os from html.parser import HTMLParser import pytest _TEMPLATE_ROOT = os.path.join( os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'app', 'templates' ) class _FormCollector(HTMLParser): """Collect forms, submit buttons, and links that represent actions.""" def __init__(self): super().__init__() self.forms = [] # list of {method, action, id} self.buttons = [] # list of {text, form_id, type} self.links = [] # list of href def handle_starttag(self, tag, attrs): attrs = dict(attrs) if tag == 'form': self.forms.append( { 'method': attrs.get('method', 'get').upper(), 'action': attrs.get('action', ''), 'id': attrs.get('id', ''), } ) elif tag == 'button': self.buttons.append( { 'type': attrs.get('type', 'submit'), 'form': attrs.get('form', ''), 'text': attrs.get('value', ''), } ) elif tag == 'input': if attrs.get('type') in ('submit', 'button', 'image'): self.buttons.append( { 'type': attrs.get('type', 'submit'), 'form': attrs.get('form', ''), 'text': attrs.get('value', ''), 'name': attrs.get('name', ''), } ) elif tag == 'a' and 'href' in attrs: href = attrs['href'] if href and not href.startswith('#') and not href.startswith('javascript:'): self.links.append(href) # --------------------------------------------------------------------------- # Each page route renders correctly with form elements # --------------------------------------------------------------------------- _PAGE_LIST = [ '/', '/auth/login', '/teams', '/matches/calendar', '/users/profile', '/users/profile/edit', '/users/contracts', '/tryouts', '/evaluations', ] @pytest.mark.parametrize('route', _PAGE_LIST) def test_page_returns_ok(app, client, route): """Unauthenticated access should redirect to login for all but public pages.""" resp = client.get(route, follow_redirects=True) # Unauthenticated access should either serve the page or redirect to login assert resp.status_code in (200, 301, 302), f'{route} returned {resp.status_code}' def test_login_form_submits_correctly(app, client, make_user): """The login form works end-to-end with valid credentials.""" user_id = make_user('player', password='Password123') with app.app_context(): from app.extensions import db from app.models import User username = db.session.get(User, user_id).username # GET the login page to ensure form is there resp = client.get('/auth/login') assert resp.status_code == 200 html = resp.data.decode('utf-8') assert 'name="username"' in html assert 'name="password"' in html assert 'type="password"' in html # POST valid credentials resp = client.post( '/auth/login', data={ 'username': username, 'password': 'Password123', }, follow_redirects=False, ) assert resp.status_code in (301, 302), f'Login did not redirect: {resp.status_code}' def test_logout_form_works(app, client, as_role): """The logout form works for an authenticated user.""" as_role('player') resp = client.post('/auth/logout', follow_redirects=False) assert resp.status_code in (301, 302), f'Logout did not redirect: {resp.status_code}' # --------------------------------------------------------------------------- # Each page renders forms for the appropriate role # --------------------------------------------------------------------------- def test_register_form_has_required_fields(app, client): """The registration form shows required fields.""" resp = client.get('/auth/register') assert resp.status_code == 200 html = resp.data.decode('utf-8') for field in ('username', 'password', 'confirm_password', 'full_name', 'email'): assert field in html, f'Registration form missing field: {field}' def test_register_form_validates_password(app, client): """Registration rejects weak passwords.""" resp = client.post( '/auth/register', data={ 'username': 'newuser', 'password': 'short', 'confirm_password': 'short', 'full_name': 'New User', 'email': 'new@example.test', }, follow_redirects=True, ) # Should return the registration page with an error assert resp.status_code == 200 assert b'password' in resp.data.lower() or b'short' in resp.data def test_create_tryout_form_validates(app, client, as_role): """Admin can see the tryout creation form.""" as_role('admin') resp = client.get('/tryouts/create', follow_redirects=True) # Admin should be able to see the create page (but it might redirect to dashboard) assert resp.status_code == 200