# retoor import hashlib import time from datetime import datetime, timedelta, timezone from playwright.sync_api import expect from tests.conftest import BASE_URL from devplacepy.database import get_table from devplacepy.utils import hash_password, generate_uid from uuid import uuid4 from datetime import datetime, timezone import requests def _seed_news_seo(): from datetime import datetime, timezone from devplacepy.database import get_table from devplacepy.utils import generate_uid, make_combined_slug uid = generate_uid() title = f"SEO Test News Article {uid.split('-')[-1]}" slug = make_combined_slug(title, uid) get_table("news").insert( { "deleted_at": None, "deleted_by": None, "uid": uid, "slug": slug, "title": title, "description": "A seeded news article for SEO tests.", "content": "Body content for the seeded article.", "url": "https://example.com/article", "source_name": "ExampleSource", "status": "published", "synced_at": datetime.now(timezone.utc).isoformat(), "show_on_landing": 0, "grade": 8, } ) return slug, uid def _seed_owner(): from datetime import datetime, timezone from uuid import uuid4 from devplacepy.database import get_table uid = str(uuid4()) get_table("users").insert( { "uid": uid, "username": f"seo_{uid[:8]}", "email": f"{uid[:8]}@seo.test", "password_hash": "x", "role": "Member", "is_active": True, "created_at": datetime.now(timezone.utc).isoformat(), } ) return uid def _seed_post_seo(image=None): from datetime import datetime, timezone from uuid import uuid4 from devplacepy.database import get_table from devplacepy.utils import make_combined_slug owner = _seed_owner() uid = str(uuid4()) slug = make_combined_slug("SEO Detail Post", uid) get_table("posts").insert( { "deleted_at": None, "deleted_by": None, "uid": uid, "user_uid": owner, "slug": slug, "title": "SEO Detail Post", "content": "Body text for the SEO detail post.", "topic": "general", "project_uid": None, "image": image, "stars": 0, "created_at": datetime.now(timezone.utc).isoformat(), } ) return slug, uid def _seed_gist(): from datetime import datetime, timezone from uuid import uuid4 from devplacepy.database import get_table from devplacepy.utils import make_combined_slug owner = _seed_owner() uid = str(uuid4()) slug = make_combined_slug("SEO Detail Gist", uid) get_table("gists").insert( { "deleted_at": None, "deleted_by": None, "uid": uid, "user_uid": owner, "slug": slug, "title": "SEO Detail Gist", "description": "Gist description for SEO tests.", "source_code": "print('seo')", "language": "python", "stars": 0, "created_at": datetime.now(timezone.utc).isoformat(), } ) return slug, uid def _seed_project(): from datetime import datetime, timezone from uuid import uuid4 from devplacepy.database import get_table from devplacepy.utils import make_combined_slug owner = _seed_owner() uid = str(uuid4()) slug = make_combined_slug("SEO Detail Project", uid) get_table("projects").insert( { "deleted_at": None, "deleted_by": None, "uid": uid, "user_uid": owner, "slug": slug, "title": "SEO Detail Project", "description": "Project description for SEO tests.", "project_type": "software", "platforms": "Linux", "status": "Released", "stars": 0, "created_at": datetime.now(timezone.utc).isoformat(), } ) return slug, uid def _seed_news_image(news_uid): from devplacepy.database import get_table get_table("news_images").insert( { "deleted_at": None, "deleted_by": None, "news_uid": news_uid, "url": "https://example.com/seo-news-image.jpg", } ) def _seed_feed_posts(count): from datetime import datetime, timezone, timedelta from uuid import uuid4 from devplacepy.database import get_table from devplacepy.utils import make_combined_slug owner = _seed_owner() topic = f"seopag{owner[:8]}" base = datetime(2026, 1, 1, tzinfo=timezone.utc) posts = get_table("posts") for i in range(count): uid = str(uuid4()) posts.insert( { "deleted_at": None, "deleted_by": None, "uid": uid, "user_uid": owner, "slug": make_combined_slug(f"seo pag {i}", uid), "title": None, "content": f"seo pag post {i}", "topic": topic, "project_uid": None, "image": None, "stars": 0, "created_at": (base - timedelta(seconds=i)).isoformat(), } ) return topic 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_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')") expect(page.locator("text=Invalid email or password")).to_be_visible() 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_next_redirects_to_target(page, app_server, seeded_db): page.goto(f"{BASE_URL}/auth/login?next=/gists", wait_until="domcontentloaded") page.fill("#email", seeded_db["alice"]["email"]) page.fill("#password", seeded_db["alice"]["password"]) page.click("button:has-text('Sign in')") page.wait_for_url("**/gists", wait_until="domcontentloaded") def test_login_next_rejects_external(page, app_server, seeded_db): page.goto( f"{BASE_URL}/auth/login?next=https://evil.example.com", wait_until="domcontentloaded", ) page.fill("#email", seeded_db["alice"]["email"]) page.fill("#password", seeded_db["alice"]["password"]) page.click("button:has-text('Sign in')") page.wait_for_url("**/feed", wait_until="domcontentloaded") def test_landing_nav_to_login(page, app_server): page.goto(f"{BASE_URL}/", wait_until="domcontentloaded") page.click("a:has-text('Login')") page.wait_for_url(f"{BASE_URL}/auth/login", wait_until="domcontentloaded") assert page.is_visible("h2:has-text('Welcome Back')") def test_landing_log_in_link(page, app_server): page.goto(f"{BASE_URL}/", wait_until="domcontentloaded") page.click("text=Log In") page.wait_for_url(f"{BASE_URL}/auth/login", wait_until="domcontentloaded") def test_login_page_is_noindex(page, app_server): page.goto(f"{BASE_URL}/auth/login", wait_until="domcontentloaded") meta = page.locator('meta[name="robots"]') content = meta.get_attribute("content") assert content and "noindex" in content