""" Admin login / session tests. Scenarios covered: 1. Correct credentials → redirected away from /login (dashboard renders) 2. Wrong password → error text "Invalid username or password." stays on /login 3. Lockout (5 consecutive bad attempts) → API returns 423; skipped for UI (covered in API unit tests; creating a throwaway user risks collateral damage) 4. Logout → redirected back to /login 5. Session persistence: page reload while logged in → stays on dashboard """ import pytest pytestmark = pytest.mark.ui # ── 1. Successful login ────────────────────────────────────────────────────── def test_login_success_redirects_to_dashboard(page, webui_base, admin_user, admin_password): """Valid credentials navigate away from /login.""" page.goto(f"{webui_base}/login") page.wait_for_load_state('networkidle') page.fill('input[autocomplete="username"]', admin_user) page.fill('input[autocomplete="current-password"]', admin_password) page.click('button[type="submit"]') page.wait_for_url(lambda url: '/login' not in url, timeout=10000) assert '/login' not in page.url def test_login_success_shows_dashboard_heading(page, webui_base, admin_user, admin_password): """After login the page title/heading contains 'Dashboard' or 'Personal Internet Cell'.""" page.goto(f"{webui_base}/login") page.fill('input[autocomplete="username"]', admin_user) page.fill('input[autocomplete="current-password"]', admin_password) page.click('button[type="submit"]') page.wait_for_url(lambda url: '/login' not in url, timeout=10000) page.wait_for_load_state('networkidle') # The sidebar renders the app title twice (mobile + desktop); use first. assert ( page.locator('h1:has-text("Personal Internet Cell")').first.is_visible() or page.locator('h1:has-text("Dashboard")').first.is_visible() ) # ── 2. Wrong password ──────────────────────────────────────────────────────── def test_login_wrong_password_shows_error(page, webui_base, admin_user): """Wrong password keeps user on /login and shows an error message.""" page.goto(f"{webui_base}/login") page.wait_for_load_state('networkidle') page.fill('input[autocomplete="username"]', admin_user) page.fill('input[autocomplete="current-password"]', 'WrongPassword999!') page.click('button[type="submit"]') # Login.jsx renders the error in a
with class text-red-400 page.wait_for_selector('text=Invalid username or password.', timeout=5000) assert '/login' in page.url def test_login_wrong_password_error_text_exact(page, webui_base, admin_user): """The exact error message from Login.jsx is shown (not a generic network error).""" page.goto(f"{webui_base}/login") page.fill('input[autocomplete="username"]', admin_user) page.fill('input[autocomplete="current-password"]', 'BadPass0000!') page.click('button[type="submit"]') error_el = page.wait_for_selector('p.text-red-400', timeout=5000) assert 'Invalid username' in error_el.inner_text() # ── 3. Lockout (deferred to API layer) ────────────────────────────────────── def test_login_lockout_deferred(): """ Lockout behavior (HTTP 423 → 'Account locked' banner) is covered by the API-layer unit tests (test_auth_routes.py). Creating a throwaway account purely to lock it in the browser risks side-effects; skip here. """ pytest.skip("Lockout UI scenario deferred — covered in test_auth_routes.py") # ── 4. Logout ──────────────────────────────────────────────────────────────── def test_logout_redirects_to_login(admin_page, webui_base): """Clicking 'Sign out' in the sidebar redirects to /login.""" page = admin_page from helpers.playwright_login import do_logout do_logout(page, webui_base) assert '/login' in page.url def test_logout_clears_session(admin_page, webui_base): """After logout, navigating to '/' redirects back to /login (no lingering session).""" page = admin_page from helpers.playwright_login import do_logout do_logout(page, webui_base) page.goto(f"{webui_base}/") # React auth check is async — wait for the redirect to /login try: page.wait_for_url(lambda url: '/login' in url, timeout=8000) except Exception: pass assert '/login' in page.url # ── 5. Session persistence ─────────────────────────────────────────────────── def test_session_persists_after_page_reload(admin_page, webui_base): """Reloading the page while logged in should keep the user authenticated.""" page = admin_page page.reload() page.wait_for_load_state('networkidle') assert '/login' not in page.url def test_session_persists_after_navigating_back(admin_page, webui_base): """Browser back-navigation from an inner page should not trigger a re-login.""" page = admin_page page.goto(f"{webui_base}/settings") page.wait_for_load_state('networkidle') page.go_back() page.wait_for_load_state('networkidle') assert '/login' not in page.url