mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-23 07:11:09 -05:00
Add standardized HTMX conventions, interaction patterns, and migration guide for ThrillWiki UX
This commit is contained in:
280
backend/tests/e2e/test_user_registration.py
Normal file
280
backend/tests/e2e/test_user_registration.py
Normal file
@@ -0,0 +1,280 @@
|
||||
"""
|
||||
E2E tests for user registration and authentication flows.
|
||||
|
||||
These tests verify the complete user journey for registration,
|
||||
login, and account management using Playwright for browser automation.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestUserRegistration:
|
||||
"""E2E tests for user registration flow."""
|
||||
|
||||
def test__registration_page__displays_form(self, page: Page, live_server):
|
||||
"""Test registration page displays the registration form."""
|
||||
page.goto(f"{live_server.url}/accounts/signup/")
|
||||
|
||||
# Verify form fields are visible
|
||||
expect(page.get_by_label("Username")).to_be_visible()
|
||||
expect(page.get_by_label("Email")).to_be_visible()
|
||||
expect(page.get_by_label("Password", exact=False).first).to_be_visible()
|
||||
|
||||
def test__registration__valid_data__creates_account(self, page: Page, live_server):
|
||||
"""Test registration with valid data creates an account."""
|
||||
page.goto(f"{live_server.url}/accounts/signup/")
|
||||
|
||||
# Fill registration form
|
||||
page.get_by_label("Username").fill("e2e_newuser")
|
||||
page.get_by_label("Email").fill("e2e_newuser@example.com")
|
||||
|
||||
# Handle password fields (may be "Password" and "Confirm Password" or similar)
|
||||
password_fields = page.locator("input[type='password']")
|
||||
if password_fields.count() >= 2:
|
||||
password_fields.nth(0).fill("SecurePass123!")
|
||||
password_fields.nth(1).fill("SecurePass123!")
|
||||
else:
|
||||
password_fields.first.fill("SecurePass123!")
|
||||
|
||||
# Submit form
|
||||
page.get_by_role("button", name="Sign Up").click()
|
||||
|
||||
# Should redirect to success page or login
|
||||
page.wait_for_url("**/*", timeout=5000)
|
||||
|
||||
def test__registration__duplicate_username__shows_error(
|
||||
self, page: Page, live_server, regular_user
|
||||
):
|
||||
"""Test registration with duplicate username shows error."""
|
||||
page.goto(f"{live_server.url}/accounts/signup/")
|
||||
|
||||
# Try to register with existing username
|
||||
page.get_by_label("Username").fill("testuser")
|
||||
page.get_by_label("Email").fill("different@example.com")
|
||||
|
||||
password_fields = page.locator("input[type='password']")
|
||||
if password_fields.count() >= 2:
|
||||
password_fields.nth(0).fill("SecurePass123!")
|
||||
password_fields.nth(1).fill("SecurePass123!")
|
||||
else:
|
||||
password_fields.first.fill("SecurePass123!")
|
||||
|
||||
page.get_by_role("button", name="Sign Up").click()
|
||||
|
||||
# Should show error message
|
||||
error = page.locator(".error, .errorlist, [role='alert']")
|
||||
expect(error.first).to_be_visible()
|
||||
|
||||
def test__registration__weak_password__shows_error(self, page: Page, live_server):
|
||||
"""Test registration with weak password shows validation error."""
|
||||
page.goto(f"{live_server.url}/accounts/signup/")
|
||||
|
||||
page.get_by_label("Username").fill("e2e_weakpass")
|
||||
page.get_by_label("Email").fill("e2e_weakpass@example.com")
|
||||
|
||||
password_fields = page.locator("input[type='password']")
|
||||
if password_fields.count() >= 2:
|
||||
password_fields.nth(0).fill("123")
|
||||
password_fields.nth(1).fill("123")
|
||||
else:
|
||||
password_fields.first.fill("123")
|
||||
|
||||
page.get_by_role("button", name="Sign Up").click()
|
||||
|
||||
# Should show password validation error
|
||||
error = page.locator(".error, .errorlist, [role='alert']")
|
||||
expect(error.first).to_be_visible()
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestUserLogin:
|
||||
"""E2E tests for user login flow."""
|
||||
|
||||
def test__login_page__displays_form(self, page: Page, live_server):
|
||||
"""Test login page displays the login form."""
|
||||
page.goto(f"{live_server.url}/accounts/login/")
|
||||
|
||||
expect(page.get_by_label("Username")).to_be_visible()
|
||||
expect(page.get_by_label("Password")).to_be_visible()
|
||||
expect(page.get_by_role("button", name="Sign In")).to_be_visible()
|
||||
|
||||
def test__login__valid_credentials__authenticates(
|
||||
self, page: Page, live_server, regular_user
|
||||
):
|
||||
"""Test login with valid credentials authenticates user."""
|
||||
page.goto(f"{live_server.url}/accounts/login/")
|
||||
|
||||
page.get_by_label("Username").fill("testuser")
|
||||
page.get_by_label("Password").fill("testpass123")
|
||||
page.get_by_role("button", name="Sign In").click()
|
||||
|
||||
# Should redirect away from login page
|
||||
page.wait_for_url("**/*")
|
||||
expect(page).not_to_have_url("**/login/**")
|
||||
|
||||
def test__login__invalid_credentials__shows_error(self, page: Page, live_server):
|
||||
"""Test login with invalid credentials shows error."""
|
||||
page.goto(f"{live_server.url}/accounts/login/")
|
||||
|
||||
page.get_by_label("Username").fill("nonexistent")
|
||||
page.get_by_label("Password").fill("wrongpass")
|
||||
page.get_by_role("button", name="Sign In").click()
|
||||
|
||||
# Should show error message
|
||||
error = page.locator(".error, .errorlist, [role='alert'], .alert-danger")
|
||||
expect(error.first).to_be_visible()
|
||||
|
||||
def test__login__remember_me__checkbox_present(self, page: Page, live_server):
|
||||
"""Test login page has remember me checkbox."""
|
||||
page.goto(f"{live_server.url}/accounts/login/")
|
||||
|
||||
remember_me = page.locator(
|
||||
"input[name='remember'], input[type='checkbox'][id*='remember']"
|
||||
)
|
||||
|
||||
if remember_me.count() > 0:
|
||||
expect(remember_me.first).to_be_visible()
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestUserLogout:
|
||||
"""E2E tests for user logout flow."""
|
||||
|
||||
def test__logout__clears_session(self, auth_page: Page, live_server):
|
||||
"""Test logout clears user session."""
|
||||
# User is already logged in via auth_page fixture
|
||||
|
||||
# Find and click logout button/link
|
||||
logout = auth_page.locator(
|
||||
"a[href*='logout'], button:has-text('Log Out'), button:has-text('Sign Out')"
|
||||
)
|
||||
|
||||
if logout.count() > 0:
|
||||
logout.first.click()
|
||||
|
||||
# Should be logged out
|
||||
auth_page.wait_for_url("**/*")
|
||||
|
||||
# Try to access protected page
|
||||
auth_page.goto(f"{live_server.url}/accounts/profile/")
|
||||
|
||||
# Should redirect to login
|
||||
expect(auth_page).to_have_url("**/login/**")
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestPasswordReset:
|
||||
"""E2E tests for password reset flow."""
|
||||
|
||||
def test__password_reset_page__displays_form(self, page: Page, live_server):
|
||||
"""Test password reset page displays the form."""
|
||||
page.goto(f"{live_server.url}/accounts/password/reset/")
|
||||
|
||||
email_input = page.locator(
|
||||
"input[type='email'], input[name='email']"
|
||||
)
|
||||
expect(email_input.first).to_be_visible()
|
||||
|
||||
def test__password_reset__valid_email__shows_confirmation(
|
||||
self, page: Page, live_server, regular_user
|
||||
):
|
||||
"""Test password reset with valid email shows confirmation."""
|
||||
page.goto(f"{live_server.url}/accounts/password/reset/")
|
||||
|
||||
email_input = page.locator("input[type='email'], input[name='email']")
|
||||
email_input.first.fill("testuser@example.com")
|
||||
|
||||
page.get_by_role("button", name="Reset Password").click()
|
||||
|
||||
# Should show confirmation message
|
||||
page.wait_for_timeout(500)
|
||||
|
||||
# Look for success message or confirmation page
|
||||
success = page.locator(
|
||||
".success, .alert-success, [role='alert']"
|
||||
)
|
||||
|
||||
# Or check URL changed to done page
|
||||
if success.count() == 0:
|
||||
expect(page).to_have_url("**/done/**")
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestUserProfile:
|
||||
"""E2E tests for user profile management."""
|
||||
|
||||
def test__profile_page__displays_user_info(self, auth_page: Page, live_server):
|
||||
"""Test profile page displays user information."""
|
||||
auth_page.goto(f"{live_server.url}/accounts/profile/")
|
||||
|
||||
# Should display username
|
||||
expect(auth_page.get_by_text("testuser")).to_be_visible()
|
||||
|
||||
def test__profile_page__edit_profile_link(self, auth_page: Page, live_server):
|
||||
"""Test profile page has edit profile link/button."""
|
||||
auth_page.goto(f"{live_server.url}/accounts/profile/")
|
||||
|
||||
edit_link = auth_page.locator(
|
||||
"a[href*='edit'], button:has-text('Edit')"
|
||||
)
|
||||
|
||||
if edit_link.count() > 0:
|
||||
expect(edit_link.first).to_be_visible()
|
||||
|
||||
def test__profile_edit__updates_info(self, auth_page: Page, live_server):
|
||||
"""Test editing profile updates user information."""
|
||||
auth_page.goto(f"{live_server.url}/accounts/profile/edit/")
|
||||
|
||||
# Find bio/about field if present
|
||||
bio_field = auth_page.locator(
|
||||
"textarea[name='bio'], textarea[name='about']"
|
||||
)
|
||||
|
||||
if bio_field.count() > 0:
|
||||
bio_field.first.fill("Updated bio from E2E test")
|
||||
|
||||
auth_page.get_by_role("button", name="Save").click()
|
||||
|
||||
# Should redirect back to profile
|
||||
auth_page.wait_for_url("**/profile/**")
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestProtectedRoutes:
|
||||
"""E2E tests for protected route access."""
|
||||
|
||||
def test__protected_route__unauthenticated__redirects_to_login(
|
||||
self, page: Page, live_server
|
||||
):
|
||||
"""Test accessing protected route redirects to login."""
|
||||
page.goto(f"{live_server.url}/accounts/profile/")
|
||||
|
||||
# Should redirect to login
|
||||
expect(page).to_have_url("**/login/**")
|
||||
|
||||
def test__protected_route__authenticated__allows_access(
|
||||
self, auth_page: Page, live_server
|
||||
):
|
||||
"""Test authenticated user can access protected routes."""
|
||||
auth_page.goto(f"{live_server.url}/accounts/profile/")
|
||||
|
||||
# Should not redirect to login
|
||||
expect(auth_page).not_to_have_url("**/login/**")
|
||||
|
||||
def test__admin_route__regular_user__denied(self, auth_page: Page, live_server):
|
||||
"""Test regular user cannot access admin routes."""
|
||||
auth_page.goto(f"{live_server.url}/admin/")
|
||||
|
||||
# Should show login or forbidden
|
||||
# Admin login page or 403
|
||||
|
||||
def test__moderator_route__moderator__allows_access(
|
||||
self, mod_page: Page, live_server
|
||||
):
|
||||
"""Test moderator can access moderation routes."""
|
||||
mod_page.goto(f"{live_server.url}/moderation/")
|
||||
|
||||
# Should not redirect to login (moderator has access)
|
||||
expect(mod_page).not_to_have_url("**/login/**")
|
||||
Reference in New Issue
Block a user