|
import hashlib
|
|
import time
|
|
from datetime import datetime, timedelta, timezone
|
|
from tests.conftest import BASE_URL
|
|
from devplacepy.database import get_table
|
|
from devplacepy.utils import hash_password, generate_uid
|
|
|
|
|
|
def test_forgot_password_page_loads(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/forgot-password", wait_until="domcontentloaded")
|
|
assert page.is_visible("input#email")
|
|
assert page.is_visible("button:has-text('Send Reset Link')")
|
|
|
|
|
|
def test_forgot_password_submit_shows_sent(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/forgot-password", wait_until="domcontentloaded")
|
|
page.fill("#email", "anybody@example.com")
|
|
page.click("button:has-text('Send Reset Link')")
|
|
page.locator(".auth-success").wait_for(state="visible")
|
|
|
|
|
|
def test_reset_password_page_loads(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/reset-password/sometoken", wait_until="domcontentloaded")
|
|
assert page.is_visible("input#password")
|
|
assert page.is_visible("input#confirm_password")
|
|
|
|
|
|
def test_reset_password_mismatch_shows_error(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/reset-password/sometoken", wait_until="domcontentloaded")
|
|
page.fill("#password", "abcdef1")
|
|
page.fill("#confirm_password", "different1")
|
|
page.click("button:has-text('Reset Password')")
|
|
page.locator(".auth-error").wait_for(state="visible")
|
|
|
|
|
|
def test_reset_password_full_flow(page, app_server):
|
|
uname = f"reset_{int(time.time() * 1000)}"
|
|
email = f"{uname}@t.dev"
|
|
uid = generate_uid()
|
|
get_table("users").insert({
|
|
"uid": uid, "username": uname, "email": email,
|
|
"password_hash": hash_password("oldpass123"),
|
|
"bio": "", "location": "", "git_link": "", "website": "",
|
|
"role": "Member", "is_active": True, "level": 1, "xp": 0, "stars": 0,
|
|
"created_at": datetime.now(timezone.utc).isoformat(),
|
|
})
|
|
token = f"tok{uid[:8]}"
|
|
get_table("password_resets").insert({
|
|
"uid": generate_uid(), "user_uid": uid,
|
|
"token": hashlib.sha256(token.encode()).hexdigest(),
|
|
"expires_at": (datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
|
|
"used": False, "created_at": datetime.now(timezone.utc).isoformat(),
|
|
})
|
|
page.goto(f"{BASE_URL}/auth/reset-password/{token}", wait_until="domcontentloaded")
|
|
page.fill("#password", "newpass456")
|
|
page.fill("#confirm_password", "newpass456")
|
|
page.click("button:has-text('Reset Password')")
|
|
page.wait_for_url("**/auth/login", wait_until="domcontentloaded")
|
|
page.fill("#email", email)
|
|
page.fill("#password", "newpass456")
|
|
page.click("button:has-text('Sign in')")
|
|
page.wait_for_url("**/feed", wait_until="domcontentloaded")
|
|
assert "/feed" in page.url
|
|
|
|
|
|
def test_signup_page_loads(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
assert page.is_visible("h2:has-text('Join the Community')")
|
|
assert page.is_visible("#username")
|
|
assert page.is_visible("#email")
|
|
assert page.is_visible("#password")
|
|
assert page.is_visible("#confirm_password")
|
|
|
|
|
|
def test_signup_success(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "fresh_user")
|
|
page.fill("#email", "fresh@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "secret123")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
|
|
assert page.is_visible("text=fresh_user")
|
|
|
|
|
|
def test_signup_existing_username(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "dup_user")
|
|
page.fill("#email", "dup1@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "secret123")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
|
|
|
|
page.context.clear_cookies()
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "dup_user")
|
|
page.fill("#email", "dup2@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "secret123")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_timeout(300)
|
|
assert page.is_visible("text=Username already taken")
|
|
|
|
|
|
def test_signup_existing_email(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "email_dup1")
|
|
page.fill("#email", "sameemail@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "secret123")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
|
|
|
|
page.context.clear_cookies()
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "email_dup2")
|
|
page.fill("#email", "sameemail@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "secret123")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_timeout(300)
|
|
assert page.is_visible("text=Email already registered")
|
|
|
|
|
|
def test_signup_password_mismatch(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "mismatch_user")
|
|
page.fill("#email", "mismatch@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "different456")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_timeout(300)
|
|
assert page.is_visible("text=Passwords do not match")
|
|
|
|
|
|
def test_signup_short_password(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "shortpw_user")
|
|
page.fill("#email", "shortpw@test.devplace")
|
|
page.fill("#password", "ab")
|
|
page.fill("#confirm_password", "ab")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_timeout(300)
|
|
assert page.is_visible("text=Password must be at least 6 characters")
|
|
|
|
|
|
def test_signup_invalid_username(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "ab")
|
|
page.fill("#email", "shortname@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "secret123")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_timeout(300)
|
|
assert page.is_visible("text=Username must be between 3 and 32 characters")
|
|
|
|
|
|
def test_login_page_loads(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/login", wait_until="domcontentloaded")
|
|
assert page.is_visible("h2:has-text('Welcome Back')")
|
|
assert page.is_visible("#email")
|
|
assert page.is_visible("#password")
|
|
assert page.is_visible("button:has-text('Sign in')")
|
|
|
|
|
|
def test_login_success(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "login_user")
|
|
page.fill("#email", "login@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "secret123")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
|
|
|
|
page.goto(f"{BASE_URL}/auth/logout", wait_until="domcontentloaded")
|
|
|
|
page.goto(f"{BASE_URL}/auth/login", wait_until="domcontentloaded")
|
|
page.fill("#email", "login@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.click("button:has-text('Sign in')")
|
|
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
|
|
assert page.is_visible("text=login_user")
|
|
|
|
|
|
def test_login_wrong_password(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "wrongpw_user")
|
|
page.fill("#email", "wrongpw@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "secret123")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
|
|
|
|
page.goto(f"{BASE_URL}/auth/logout", wait_until="domcontentloaded")
|
|
|
|
page.goto(f"{BASE_URL}/auth/login", wait_until="domcontentloaded")
|
|
page.fill("#email", "wrongpw@test.devplace")
|
|
page.fill("#password", "badpassword")
|
|
page.click("button:has-text('Sign in')")
|
|
page.wait_for_timeout(300)
|
|
assert page.is_visible("text=Invalid email or password")
|
|
|
|
|
|
def test_login_nonexistent_email(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/login", wait_until="domcontentloaded")
|
|
page.fill("#email", "nobody@nowhere.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.click("button:has-text('Sign in')")
|
|
page.wait_for_timeout(300)
|
|
assert page.is_visible("text=Invalid email or password")
|
|
|
|
|
|
def test_login_remember_me(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "remember_user")
|
|
page.fill("#email", "remember@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "secret123")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
|
|
|
|
page.goto(f"{BASE_URL}/auth/logout", wait_until="domcontentloaded")
|
|
|
|
page.goto(f"{BASE_URL}/auth/login", wait_until="domcontentloaded")
|
|
page.fill("#email", "remember@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.check("input[name='remember_me']")
|
|
page.click("button:has-text('Sign in')")
|
|
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
|
|
assert page.is_visible("text=remember_user")
|
|
|
|
|
|
def test_logout(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.fill("#username", "logout_user")
|
|
page.fill("#email", "logout@test.devplace")
|
|
page.fill("#password", "secret123")
|
|
page.fill("#confirm_password", "secret123")
|
|
page.click("button:has-text('Create account')")
|
|
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
|
|
|
|
page.goto(f"{BASE_URL}/auth/logout", wait_until="domcontentloaded")
|
|
page.wait_for_url("**/", wait_until="domcontentloaded")
|
|
assert page.is_visible("text=Join DevPlace Free")
|
|
|
|
|
|
def test_signup_link_from_login(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/login", wait_until="domcontentloaded")
|
|
page.click("a:has-text('Create new account')")
|
|
page.wait_for_url("**/auth/signup", wait_until="domcontentloaded")
|
|
|
|
|
|
def test_login_link_from_signup(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
page.click("a:has-text('Sign in instead')")
|
|
page.wait_for_url("**/auth/login", wait_until="domcontentloaded")
|
|
|
|
|
|
def test_password_toggle(page, app_server):
|
|
page.goto(f"{BASE_URL}/auth/signup", wait_until="domcontentloaded")
|
|
pw_input = page.locator("#password")
|
|
toggle = page.locator(".auth-toggle-pw").first
|
|
assert pw_input.get_attribute("type") == "password"
|
|
toggle.click()
|
|
assert pw_input.get_attribute("type") == "text"
|
|
toggle.click()
|
|
assert pw_input.get_attribute("type") == "password"
|