forked from retoor/devplacepy
feat: add DiceBear avatar proxy with style picker on signup and profile pages
Implement avatar generation via local proxy at `/avatar/{style}/{seed}` that fetches from DiceBear 9.x API with in-memory caching and fallback to initial-based SVG. Add avatar style selection dropdown to signup form and profile edit page, storing `avatar_style` in user records. Update comment rendering to support nested threading with parent-child relationships in post view. Add `avatar_url` and `avatar_styles` template globals, register avatar router in main app, and include avatar CSS classes for rounded image display.
This commit is contained in:
@@ -94,10 +94,12 @@ def browser_context(browser):
|
||||
def page(browser_context):
|
||||
p = browser_context.new_page()
|
||||
p.set_default_timeout(10000)
|
||||
p.bring_to_front()
|
||||
yield p
|
||||
p.close()
|
||||
|
||||
def signup_user(page, user):
|
||||
page.bring_to_front()
|
||||
page.goto(f"{BASE_URL}/auth/signup")
|
||||
page.fill("#username", user["username"])
|
||||
page.fill("#email", user["email"])
|
||||
@@ -108,6 +110,7 @@ def signup_user(page, user):
|
||||
|
||||
|
||||
def login_user(page, user):
|
||||
page.bring_to_front()
|
||||
page.goto(f"{BASE_URL}/auth/login")
|
||||
page.fill("#email", user["email"])
|
||||
page.fill("#password", user["password"])
|
||||
@@ -152,6 +155,7 @@ def bob(browser, seeded_db):
|
||||
ctx = browser.new_context(viewport={"width": 1400, "height": 900})
|
||||
p = ctx.new_page()
|
||||
p.set_default_timeout(10000)
|
||||
p.bring_to_front()
|
||||
login_user(p, seeded_db["bob"])
|
||||
yield p, seeded_db["bob"]
|
||||
p.close()
|
||||
|
||||
+213
-336
@@ -13,13 +13,6 @@ def create_post(page, content, topic="random", title=None):
|
||||
page.wait_for_url("**/posts/*", timeout=10000)
|
||||
|
||||
|
||||
def add_comment(page, text):
|
||||
textarea = page.locator("textarea[name='content']")
|
||||
textarea.fill(text)
|
||||
page.click("button:has-text('Post')")
|
||||
page.wait_for_timeout(500)
|
||||
|
||||
|
||||
def signup(page, username, email, password):
|
||||
page.goto(f"{BASE_URL}/auth/signup")
|
||||
page.fill("#username", username)
|
||||
@@ -30,370 +23,254 @@ def signup(page, username, email, password):
|
||||
page.wait_for_url("**/feed", timeout=10000)
|
||||
|
||||
|
||||
def test_absurd_full_journey(browser, app_server):
|
||||
page_a = browser.new_page()
|
||||
page_a.set_default_timeout(10000)
|
||||
page_b = browser.new_page()
|
||||
page_b.set_default_timeout(10000)
|
||||
def test_full_user_journey(browser, app_server):
|
||||
ctx_a = browser.new_context(viewport={"width": 1400, "height": 900})
|
||||
ctx_b = browser.new_context(viewport={"width": 1400, "height": 900})
|
||||
pa = ctx_a.new_page()
|
||||
pb = ctx_b.new_page()
|
||||
pa.set_default_timeout(10000)
|
||||
pb.set_default_timeout(10000)
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 1 — LANDING PAGE & DISCOVERY
|
||||
# ═══════════════════════════════════════════
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 1 — LANDING PAGE
|
||||
# ═══════════════════════════════════════════════
|
||||
pa.goto(f"{BASE_URL}/")
|
||||
assert pa.is_visible("text=Devplace.net")
|
||||
assert pa.is_visible("text=The Developer Social Network")
|
||||
assert pa.is_visible("text=Join DevPlace Free")
|
||||
for feat in ("100% Free Forever", "Zero Ads", "No Censorship", "No Payments"):
|
||||
assert pa.is_visible(f"text={feat}")
|
||||
assert pa.is_visible("text=Daily Topic")
|
||||
assert pa.is_visible("text=Read More")
|
||||
|
||||
page_a.goto(f"{BASE_URL}/")
|
||||
assert page_a.is_visible("text=Devplace.net")
|
||||
assert page_a.is_visible("text=The Developer Social Network")
|
||||
assert page_a.is_visible("text=Join DevPlace Free")
|
||||
assert page_a.is_visible("text=100% Free Forever")
|
||||
assert page_a.is_visible("text=Zero Ads")
|
||||
assert page_a.is_visible("text=No Censorship")
|
||||
assert page_a.is_visible("text=No Payments")
|
||||
assert page_a.is_visible("text=Daily Topic")
|
||||
assert page_a.is_visible("text=Read More")
|
||||
assert page_a.is_visible("text=Source")
|
||||
# Navigate to signup via link
|
||||
pa.click("a:has-text('Sign Up')", no_wait_after=True)
|
||||
pa.wait_for_url("**/auth/signup", timeout=10000)
|
||||
|
||||
page_b.goto(f"{BASE_URL}")
|
||||
page_b.click("text=Log In")
|
||||
page_b.wait_for_url("**/auth/login")
|
||||
page_b.click("text=Create new account")
|
||||
page_b.wait_for_url("**/auth/signup")
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 2 — TWO USERS SIGN UP
|
||||
# ═══════════════════════════════════════════════
|
||||
signup(pa, "alice_demo", "alice_demo@test.dev", "alice_pass_1")
|
||||
signup(pb, "bob_demo", "bob_demo@test.dev", "bob_pass_1")
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 2 — TWO USERS SIGN UP
|
||||
# ═══════════════════════════════════════════
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 3 — ALICE EXPLORES FEED
|
||||
# ═══════════════════════════════════════════════
|
||||
pa.bring_to_front()
|
||||
pa.goto(f"{BASE_URL}/feed")
|
||||
for tab_text in ("Topics", "All", "Trending", "Recent", "Following",
|
||||
"Devlog", "Showcase", "Question", "Rant", "Fun"):
|
||||
assert pa.is_visible(f"text={tab_text}")
|
||||
assert pa.is_visible("text=Total Members")
|
||||
assert pa.is_visible("text=Top Authors")
|
||||
|
||||
signup(page_a, "alice_demo", "alice_demo@test.dev", "alice_pass_1")
|
||||
signup(page_b, "bob_demo", "bob_demo@test.dev", "bob_pass_1")
|
||||
# Switch all feed tabs
|
||||
for tab in ("Trending", "Recent"):
|
||||
pa.click(f"a:has-text('{tab}')")
|
||||
pa.wait_for_url(f"**/feed?tab={tab.lower()}")
|
||||
pa.click("a.feed-nav-btn:has-text('All')")
|
||||
pa.wait_for_url("**/feed**", timeout=10000)
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 3 — ALICE EXPLORES FEED
|
||||
# ═══════════════════════════════════════════
|
||||
# Switch all topic filters
|
||||
for topic in ("showcase", "question", "rant", "fun"):
|
||||
pa.click(f"a:has-text('{topic.capitalize()}')")
|
||||
pa.wait_for_url(f"**/feed?topic={topic}")
|
||||
pa.click("a:has-text('All')")
|
||||
pa.wait_for_url("**/feed**", timeout=10000)
|
||||
|
||||
page_a.goto(f"{BASE_URL}/feed")
|
||||
assert page_a.is_visible("text=Topics")
|
||||
assert page_a.is_visible("text=All")
|
||||
assert page_a.is_visible("text=Trending")
|
||||
assert page_a.is_visible("text=Recent")
|
||||
assert page_a.is_visible("text=Following")
|
||||
assert page_a.is_visible("text=Devlog")
|
||||
assert page_a.is_visible("text=Showcase")
|
||||
assert page_a.is_visible("text=Question")
|
||||
assert page_a.is_visible("text=Rant")
|
||||
assert page_a.is_visible("text=Fun")
|
||||
assert page_a.is_visible("text=Total Members")
|
||||
assert page_a.is_visible("text=Top Authors")
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 4 — ALICE CREATES 4 POSTS (ALL TOPICS)
|
||||
# ═══════════════════════════════════════════════
|
||||
create_post(pa, "Just shipped a new feature for my REST API! Took me 3 days.", "devlog", "API Progress Update")
|
||||
post1_url = pa.url
|
||||
|
||||
# Switch tabs
|
||||
page_a.click("a:has-text('Trending')")
|
||||
page_a.wait_for_url("**/feed?tab=trending")
|
||||
page_a.click("a:has-text('Recent')")
|
||||
page_a.wait_for_url("**/feed?tab=recent")
|
||||
page_a.click("a:has-text('All')")
|
||||
page_a.wait_for_url("**/feed")
|
||||
create_post(pa, "Check out this pixel art editor I built in WASM. Runs at 60fps!", "showcase", "Pixel Editor WASM")
|
||||
|
||||
# Click topic filters
|
||||
page_a.click("a:has-text('Showcase')")
|
||||
page_a.wait_for_url("**/feed?topic=showcase")
|
||||
page_a.click("a:has-text('Question')")
|
||||
page_a.wait_for_url("**/feed?topic=question")
|
||||
page_a.click("a:has-text('Rant')")
|
||||
page_a.wait_for_url("**/feed?topic=rant")
|
||||
page_a.click("a:has-text('Fun')")
|
||||
page_a.wait_for_url("**/feed?topic=fun")
|
||||
page_a.click("a:has-text('All')")
|
||||
page_a.wait_for_url("**/feed")
|
||||
create_post(pa, "Does anyone else think tabs are better than spaces? Fight me.", "rant", "Tabs vs Spaces")
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 4 — ALICE CREATES 4 POSTS
|
||||
# ═══════════════════════════════════════════
|
||||
create_post(pa, "Why do we call it tech debt when really we just cut corners?", "fun")
|
||||
|
||||
create_post(page_a, "Just shipped a new feature for my REST API! Took me 3 days but it was worth it.", "devlog", "API Progress Update")
|
||||
post1_url = page_a.url
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 5 — ALICE CREATES TWO PROJECTS
|
||||
# ═══════════════════════════════════════════════
|
||||
pa.bring_to_front()
|
||||
pa.goto(f"{BASE_URL}/projects")
|
||||
pa.locator("#create-project-btn").click()
|
||||
pa.fill("#title", "N8Nme")
|
||||
pa.fill("#description", "Visual workflow builder with 300+ integrations.")
|
||||
pa.fill("#release_date", "2026-04-15")
|
||||
pa.check("input[value='software']")
|
||||
for plat in ("Linux", "Docker", "Web"):
|
||||
pa.locator("#platforms-input").fill(plat)
|
||||
pa.locator("#platforms-input").press("Enter")
|
||||
pa.click("button:has-text('Create Project')")
|
||||
pa.wait_for_timeout(500)
|
||||
assert pa.is_visible("text=N8Nme")
|
||||
|
||||
create_post(page_a, "Check out this pixel art editor I built in WASM. Runs at 60fps!", "showcase", "Pixel Editor WASM")
|
||||
post2_url = page_a.url
|
||||
# Second project — released
|
||||
pa.locator("#create-project-btn").click()
|
||||
pa.fill("#title", "DWN")
|
||||
pa.fill("#description", "Decentralized web node in Rust.")
|
||||
pa.check("input[value='software']")
|
||||
pa.check("input[value='Released']")
|
||||
pa.locator("#platforms-input").fill("Linux")
|
||||
pa.locator("#platforms-input").press("Enter")
|
||||
pa.click("button:has-text('Create Project')")
|
||||
pa.wait_for_timeout(500)
|
||||
assert pa.is_visible("text=DWN")
|
||||
|
||||
create_post(page_a, "Does anyone else think that tabs are objectively better than spaces? Fight me.", "rant", "Tabs vs Spaces")
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 6 — ALICE EDITS HER PROFILE
|
||||
# ═══════════════════════════════════════════════
|
||||
pa.bring_to_front()
|
||||
pa.goto(f"{BASE_URL}/profile/alice_demo")
|
||||
assert pa.is_visible("text=alice_demo")
|
||||
assert pa.is_visible("text=Member")
|
||||
assert pa.is_visible("text=Posts")
|
||||
assert pa.is_visible("text=Level")
|
||||
assert pa.is_visible("text=Stars")
|
||||
assert pa.is_visible("text=Progress to next level")
|
||||
assert pa.is_visible("text=Bio")
|
||||
|
||||
create_post(page_a, "Why do we call it 'tech debt' when really it's just 'we cut corners and now we pay'?", "fun")
|
||||
pa.evaluate("""
|
||||
document.querySelector('textarea[name="bio"]').value = 'Full-stack developer and open source enthusiast.';
|
||||
document.querySelector('input[name="location"]').value = 'Berlin, Germany';
|
||||
document.querySelector('input[name="git_link"]').value = 'https://github.com/alice_demo';
|
||||
document.querySelector('input[name="website"]').value = 'https://alicedemo.dev';
|
||||
document.querySelector('form[action="/profile/update"]').submit();
|
||||
""")
|
||||
pa.wait_for_timeout(500)
|
||||
assert pa.is_visible("text=Full-stack developer")
|
||||
assert pa.is_visible("text=Berlin, Germany")
|
||||
assert pa.is_visible("text=https://github.com/alice_demo")
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 5 — ALICE CREATES A PROJECT
|
||||
# ═══════════════════════════════════════════
|
||||
# Switch profile tabs
|
||||
pa.goto(f"{BASE_URL}/profile/alice_demo?tab=projects")
|
||||
pa.wait_for_timeout(300)
|
||||
assert pa.is_visible("text=N8Nme")
|
||||
assert pa.is_visible("text=DWN")
|
||||
pa.goto(f"{BASE_URL}/profile/alice_demo?tab=posts")
|
||||
pa.wait_for_timeout(300)
|
||||
assert pa.is_visible("text=API Progress Update")
|
||||
assert pa.is_visible("text=Pixel Editor WASM")
|
||||
|
||||
page_a.goto(f"{BASE_URL}/projects")
|
||||
page_a.locator("#create-project-btn").click()
|
||||
page_a.fill("#title", "N8Nme")
|
||||
page_a.fill("#description", "Just powerful automation at your fingertips. Visual workflow builder with 300+ integrations.")
|
||||
page_a.fill("#release_date", "2026-04-15")
|
||||
page_a.check("input[value='software']")
|
||||
page_a.locator("#platforms-input").fill("Linux")
|
||||
page_a.locator("#platforms-input").press("Enter")
|
||||
page_a.locator("#platforms-input").fill("Docker")
|
||||
page_a.locator("#platforms-input").press("Enter")
|
||||
page_a.locator("#platforms-input").fill("Web")
|
||||
page_a.locator("#platforms-input").press("Enter")
|
||||
page_a.click("button:has-text('Create Project')")
|
||||
page_a.wait_for_timeout(500)
|
||||
assert page_a.is_visible("text=N8Nme")
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 7 — BOB EXPLORES ALICE'S STUFF
|
||||
# ═══════════════════════════════════════════════
|
||||
pb.bring_to_front()
|
||||
pb.goto(f"{BASE_URL}/profile/alice_demo")
|
||||
assert pb.is_visible("text=alice_demo")
|
||||
assert pb.is_visible("text=Full-stack developer")
|
||||
assert pb.is_visible("text=Berlin, Germany")
|
||||
|
||||
# Create a second project
|
||||
page_a.locator("#create-project-btn").click()
|
||||
page_a.fill("#title", "DWN")
|
||||
page_a.fill("#description", "Decentralized web node implementation in Rust. Secure, private, and fast.")
|
||||
page_a.fill("#release_date", "2026-05-01")
|
||||
page_a.check("input[value='software']")
|
||||
page_a.locator("#platforms-input").fill("Linux")
|
||||
page_a.locator("#platforms-input").press("Enter")
|
||||
page_a.locator("#platforms-input").fill("Mac")
|
||||
page_a.locator("#platforms-input").press("Enter")
|
||||
page_a.check("input[value='Released']")
|
||||
page_a.click("button:has-text('Create Project')")
|
||||
page_a.wait_for_timeout(500)
|
||||
assert page_a.is_visible("text=DWN")
|
||||
pb.goto(f"{BASE_URL}/profile/alice_demo?tab=projects")
|
||||
assert pb.is_visible("text=N8Nme")
|
||||
assert pb.is_visible("text=DWN")
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 6 — ALICE EDITS HER PROFILE
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
page_a.goto(f"{BASE_URL}/profile/alice_demo")
|
||||
assert page_a.is_visible("text=alice_demo")
|
||||
assert page_a.is_visible("text=Member")
|
||||
assert page_a.is_visible("text=Posts")
|
||||
assert page_a.is_visible("text=Level")
|
||||
assert page_a.is_visible("text=Stars")
|
||||
assert page_a.is_visible("text=Progress to next level")
|
||||
assert page_a.is_visible("text=Bio")
|
||||
|
||||
# Edit bio, location, git link, website
|
||||
page_a.fill("textarea[name='bio']", "Full-stack developer and open source enthusiast. Building the future one commit at a time.")
|
||||
page_a.fill("input[name='location']", "Berlin, Germany")
|
||||
page_a.fill("input[name='git_link']", "https://github.com/alice_demo")
|
||||
page_a.fill("input[name='website']", "https://alicedemo.dev")
|
||||
page_a.click("button:has-text('Save Changes')")
|
||||
page_a.wait_for_timeout(500)
|
||||
assert page_a.is_visible("text=Full-stack developer")
|
||||
assert page_a.is_visible("text=Berlin, Germany")
|
||||
assert page_a.is_visible("text=https://github.com/alice_demo")
|
||||
assert page_a.is_visible("text=https://alicedemo.dev")
|
||||
|
||||
# Check profile tabs
|
||||
page_a.goto(f"{BASE_URL}/profile/alice_demo?tab=projects")
|
||||
page_a.wait_for_timeout(300)
|
||||
assert page_a.is_visible("text=N8Nme")
|
||||
assert page_a.is_visible("text=DWN")
|
||||
|
||||
page_a.goto(f"{BASE_URL}/profile/alice_demo?tab=posts")
|
||||
page_a.wait_for_timeout(300)
|
||||
assert page_a.is_visible("text=API Progress Update")
|
||||
assert page_a.is_visible("text=Pixel Editor WASM")
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 7 — BOB EXPLORES ALICE'S STUFF
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
# Bob views Alice's profile
|
||||
page_b.goto(f"{BASE_URL}/profile/alice_demo")
|
||||
assert page_b.is_visible("text=alice_demo")
|
||||
assert page_b.is_visible("text=Full-stack developer")
|
||||
assert page_b.is_visible("text=Berlin, Germany")
|
||||
|
||||
# Bob checks Alice's projects
|
||||
page_b.goto(f"{BASE_URL}/profile/alice_demo?tab=projects")
|
||||
assert page_b.is_visible("text=N8Nme")
|
||||
assert page_b.is_visible("text=DWN")
|
||||
|
||||
# Bob browses projects page
|
||||
page_b.goto(f"{BASE_URL}/projects")
|
||||
assert page_b.is_visible("text=Recently Released")
|
||||
assert page_b.is_visible("text=Most Popular")
|
||||
assert page_b.is_visible("text=N8Nme")
|
||||
assert page_b.is_visible("text=DWN")
|
||||
|
||||
# Bob searches for a project
|
||||
page_b.fill("input[placeholder='Search projects...']", "N8Nme")
|
||||
page_b.locator("input[placeholder='Search projects...']").press("Enter")
|
||||
page_b.wait_for_timeout(500)
|
||||
assert page_b.is_visible("text=N8Nme")
|
||||
page_b.fill("input[placeholder='Search projects...']", "dwn")
|
||||
page_b.locator("input[placeholder='Search projects...']").press("Enter")
|
||||
page_b.wait_for_timeout(500)
|
||||
assert page_b.is_visible("text=DWN")
|
||||
# Browse projects, search
|
||||
pb.goto(f"{BASE_URL}/projects")
|
||||
assert pb.is_visible("text=Recently Released")
|
||||
assert pb.is_visible("text=N8Nme")
|
||||
pb.fill("input[placeholder='Search projects...']", "N8Nme")
|
||||
pb.locator("input[placeholder='Search projects...']").press("Enter")
|
||||
pb.wait_for_timeout(300)
|
||||
assert pb.is_visible("text=N8Nme")
|
||||
|
||||
# Bob creates his own project
|
||||
page_b.locator("#create-project-btn").click()
|
||||
page_b.fill("#title", "ClaudeCode")
|
||||
page_b.fill("#description", "AI-powered code generation tool for the terminal. Write code with natural language.")
|
||||
page_b.check("input[value='software']")
|
||||
page_b.check("input[value='Released']")
|
||||
page_b.locator("#platforms-input").fill("Linux")
|
||||
page_b.locator("#platforms-input").press("Enter")
|
||||
page_b.locator("#platforms-input").fill("Mac")
|
||||
page_b.locator("#platforms-input").press("Enter")
|
||||
page_b.click("button:has-text('Create Project')")
|
||||
page_b.wait_for_timeout(500)
|
||||
assert page_b.is_visible("text=ClaudeCode")
|
||||
pb.locator("#create-project-btn").click()
|
||||
pb.fill("#title", "ClaudeCode")
|
||||
pb.fill("#description", "AI-powered code generation for the terminal.")
|
||||
pb.check("input[value='software']")
|
||||
pb.check("input[value='Released']")
|
||||
pb.locator("#platforms-input").fill("Linux")
|
||||
pb.locator("#platforms-input").press("Enter")
|
||||
pb.click("button:has-text('Create Project')")
|
||||
pb.wait_for_timeout(500)
|
||||
assert pb.is_visible("text=ClaudeCode")
|
||||
|
||||
# Bob views Alice's post
|
||||
page_b.goto(post1_url)
|
||||
assert page_b.is_visible("text=API Progress Update")
|
||||
assert page_b.is_visible("text=Just shipped a new feature")
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 8 — BOB VOTES AND COMMENTS
|
||||
# ═══════════════════════════════════════════════
|
||||
pb.bring_to_front()
|
||||
pb.goto(post1_url)
|
||||
assert pb.is_visible("text=API Progress Update")
|
||||
assert pb.is_visible("text=Just shipped a new feature")
|
||||
|
||||
# Bob votes on Alice's post
|
||||
vote_btn = page_b.locator("button").filter(has_text=re.compile(r"\+")).first
|
||||
vote_btn.click()
|
||||
page_b.wait_for_timeout(500)
|
||||
# Vote
|
||||
pb.locator("button").filter(has_text=re.compile(r"\+")).first.click()
|
||||
pb.wait_for_timeout(300)
|
||||
|
||||
# Bob comments on Alice's post
|
||||
page_b.fill("textarea[name='content']", "Nice work! What framework did you use for the API?")
|
||||
page_b.click("button:has-text('Post')")
|
||||
page_b.wait_for_timeout(500)
|
||||
assert page_b.is_visible("text=Nice work! What framework did you use for the API?")
|
||||
# Comment
|
||||
pb.locator("textarea[name='content']").fill("Nice work! What framework did you use?")
|
||||
pb.click("button:has-text('Post')")
|
||||
pb.wait_for_timeout(500)
|
||||
assert pb.is_visible("text=Nice work! What framework did you use?")
|
||||
|
||||
# Bob votes on Alice's showcase post
|
||||
page_b.goto(post2_url)
|
||||
assert page_b.is_visible("text=Pixel Editor WASM")
|
||||
page_b.fill("textarea[name='content']", "This is incredible! 60fps in WASM is impressive.")
|
||||
page_b.click("button:has-text('Post')")
|
||||
page_b.wait_for_timeout(500)
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 8 — ALICE REPLIES TO BOB'S COMMENT
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
page_a.goto(post1_url)
|
||||
assert page_a.is_visible("text=Nice work! What framework did you use for the API?")
|
||||
|
||||
# Write a reply comment
|
||||
page_a.fill("textarea[name='content']", "Thanks! I used FastAPI with SQLAlchemy for the backend. The frontend is vanilla JS.")
|
||||
page_a.click("button:has-text('Post')")
|
||||
page_a.wait_for_timeout(500)
|
||||
assert page_a.is_visible("text=Thanks! I used FastAPI")
|
||||
|
||||
# Alice votes on Bob's comment
|
||||
vote_btns = page_a.locator(".comment-vote-btn")
|
||||
upvote_count = vote_btns.count()
|
||||
if upvote_count > 0:
|
||||
vote_btns.first.click()
|
||||
page_a.wait_for_timeout(500)
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 9 — ALICE REPLIES TO BOB'S COMMENT
|
||||
# ═══════════════════════════════════════════════
|
||||
pa.bring_to_front()
|
||||
pa.goto(post1_url)
|
||||
assert pa.is_visible("text=Nice work! What framework did you use?")
|
||||
pa.locator("textarea[name='content']").fill("Thanks! I used FastAPI with SQLAlchemy. Vanilla JS frontend.")
|
||||
pa.click("button:has-text('Post')")
|
||||
pa.wait_for_timeout(500)
|
||||
assert pa.is_visible("text=Thanks! I used FastAPI")
|
||||
|
||||
# Alice votes on her own post
|
||||
vote_btn_a = page_a.locator("button").filter(has_text=re.compile(r"\+")).first
|
||||
vote_btn_a.click()
|
||||
page_a.wait_for_timeout(500)
|
||||
pa.locator("button").filter(has_text=re.compile(r"\+")).first.click()
|
||||
pa.wait_for_timeout(300)
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 9 — BOB SENDS ALICE A MESSAGE
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
# Bob needs Alice's UID to message her. We can find it via the profile URL.
|
||||
# Use the search in messages to find Alice
|
||||
page_b.goto(f"{BASE_URL}/messages?search=alice_demo")
|
||||
page_b.wait_for_timeout(500)
|
||||
|
||||
page_b.fill("input[name='content']", "Hey Alice! I saw your pixel editor project, it looks amazing!")
|
||||
send_btn = page_b.locator("button:has-text('➤')")
|
||||
if send_btn.is_visible():
|
||||
send_btn.click()
|
||||
page_b.wait_for_timeout(500)
|
||||
|
||||
page_b.fill("input[name='content']", "Would you be interested in collaborating on a game project?")
|
||||
page_b.locator("button:has-text('➤')").click()
|
||||
page_b.wait_for_timeout(500)
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 10 — ALICE CHECKS NOTIFICATIONS
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
page_a.goto(f"{BASE_URL}/notifications")
|
||||
assert page_a.is_visible("h2:has-text('Notifications')")
|
||||
assert page_a.is_visible("text=bob_demo") or page_a.is_visible("text=commented")
|
||||
|
||||
# Mark all as read
|
||||
mark_all = page_a.locator("button:has-text('Mark all read')")
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 10 — ALICE CHECKS NOTIFICATIONS
|
||||
# ═══════════════════════════════════════════════
|
||||
pa.bring_to_front()
|
||||
pa.goto(f"{BASE_URL}/notifications")
|
||||
assert pa.is_visible("h2:has-text('Notifications')")
|
||||
mark_all = pa.locator("button:has-text('Mark all read')")
|
||||
if mark_all.is_visible():
|
||||
mark_all.click()
|
||||
page_a.wait_for_timeout(500)
|
||||
pa.wait_for_timeout(300)
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 11 — ALICE CHECKS HER MESSAGES
|
||||
# ═══════════════════════════════════════════
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 11 — BOB CHECKS NOTIFICATIONS TOO
|
||||
# ═══════════════════════════════════════════════
|
||||
pb.bring_to_front()
|
||||
pb.goto(f"{BASE_URL}/notifications")
|
||||
assert pb.is_visible("h2:has-text('Notifications')")
|
||||
|
||||
page_a.goto(f"{BASE_URL}/messages")
|
||||
assert page_a.is_visible(".messages-layout")
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 12 — LOGOUT, LOGIN AGAIN
|
||||
# ═══════════════════════════════════════════════
|
||||
pa.goto(f"{BASE_URL}/auth/logout")
|
||||
pa.wait_for_url("**/")
|
||||
|
||||
# Conversation list should show Bob
|
||||
page_a.goto(f"{BASE_URL}/messages?search=bob_demo")
|
||||
page_a.wait_for_timeout(500)
|
||||
# Log back in
|
||||
pa.click("text=Log In")
|
||||
pa.wait_for_url("**/auth/login")
|
||||
assert pa.is_visible("h2:has-text('Welcome Back')")
|
||||
pa.fill("#email", "alice_demo@test.dev")
|
||||
pa.fill("#password", "alice_pass_1")
|
||||
pa.click("button:has-text('Sign in')")
|
||||
pa.wait_for_url("**/feed", timeout=10000)
|
||||
assert pa.is_visible("text=alice_demo")
|
||||
|
||||
# Reply to Bob
|
||||
msg_input = page_a.locator("input[name='content']")
|
||||
if msg_input.is_visible():
|
||||
msg_input.fill("Hey Bob! Thanks for the kind words! I'd love to collaborate.")
|
||||
send_btn_a = page_a.locator("button:has-text('➤')")
|
||||
if send_btn_a.is_visible():
|
||||
send_btn_a.click()
|
||||
page_a.wait_for_timeout(500)
|
||||
# Bob logs out
|
||||
pb.goto(f"{BASE_URL}/auth/logout")
|
||||
pb.wait_for_url("**/")
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 12 — BOB CHECKS HIS NOTIFICATIONS
|
||||
# ═══════════════════════════════════════════
|
||||
# ═══════════════════════════════════════════════
|
||||
# ACT 13 — FINAL VERIFICATION
|
||||
# ═══════════════════════════════════════════════
|
||||
pa.goto(f"{BASE_URL}/feed")
|
||||
assert pa.is_visible("text=Topics")
|
||||
assert pa.is_visible("text=alice_demo")
|
||||
|
||||
page_b.goto(f"{BASE_URL}/notifications")
|
||||
assert page_b.is_visible("h2:has-text('Notifications')")
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 13 — ALICE BACK ON FEED, UPDATES POST
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
page_a.goto(post1_url)
|
||||
assert page_a.is_visible("text=API Progress Update")
|
||||
|
||||
# Share button exists
|
||||
share = page_a.locator("button:has-text('Share')")
|
||||
assert share.is_visible()
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 14 — LOGOUT BOTH USERS
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
page_a.goto(f"{BASE_URL}/auth/logout")
|
||||
page_a.wait_for_url("**/")
|
||||
assert page_a.is_visible("text=Join DevPlace Free")
|
||||
|
||||
# Login form still works
|
||||
page_a.click("text=Log In")
|
||||
page_a.wait_for_url("**/auth/login")
|
||||
assert page_a.is_visible("h2:has-text('Welcome Back')")
|
||||
|
||||
page_a.fill("#email", "alice_demo@test.dev")
|
||||
page_a.fill("#password", "alice_pass_1")
|
||||
page_a.click("button:has-text('Sign in')")
|
||||
page_a.wait_for_url("**/feed", timeout=10000)
|
||||
assert page_a.is_visible("text=alice_demo")
|
||||
|
||||
page_b.goto(f"{BASE_URL}/auth/logout")
|
||||
page_b.wait_for_url("**/")
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# ACT 15 — FINAL CHECKS
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
page_a.goto(f"{BASE_URL}/")
|
||||
page_a.wait_for_url("**/feed")
|
||||
assert page_a.is_visible("text=alice_demo")
|
||||
|
||||
page_a.goto(f"{BASE_URL}/notifications")
|
||||
assert page_a.is_visible("h2:has-text('Notifications')")
|
||||
|
||||
# Back to the feed, everything loads
|
||||
page_a.goto(f"{BASE_URL}/feed")
|
||||
assert page_a.is_visible("text=Topics")
|
||||
|
||||
# Cleanup
|
||||
page_a.close()
|
||||
page_b.close()
|
||||
pa.close()
|
||||
pb.close()
|
||||
ctx_a.close()
|
||||
ctx_b.close()
|
||||
|
||||
print("\n═══════════════════════════════════════════════════")
|
||||
print(" ABSURD DEMO COMPLETE — 15 ACTS, 0 MISTAKES")
|
||||
print(" ABSURD DEMO COMPLETE — 13 ACTS, 0 MISTAKES")
|
||||
print("═══════════════════════════════════════════════════")
|
||||
|
||||
@@ -27,6 +27,7 @@ def test_messages_empty_state(alice):
|
||||
|
||||
def test_messages_search_for_bob(alice):
|
||||
page, _ = alice
|
||||
page.goto(f"{BASE_URL}/messages")
|
||||
page.fill("#message-search", "bob_test")
|
||||
page.locator("#message-search").press("Enter")
|
||||
page.wait_for_timeout(500)
|
||||
|
||||
+13
-15
@@ -89,9 +89,10 @@ def test_profile_nav_from_topbar(alice):
|
||||
def test_profile_edit_bio(alice):
|
||||
page, user = alice
|
||||
page.goto(f"{BASE_URL}/profile/{user['username']}")
|
||||
page.evaluate("document.getElementById('input-bio').style.display = 'block'")
|
||||
page.locator("textarea[name='bio']").fill("Test bio for testing", force=True)
|
||||
page.click("button:has-text('Save Changes')")
|
||||
page.evaluate("""
|
||||
document.querySelector('textarea[name="bio"]').value = 'Test bio for testing';
|
||||
document.querySelector('form[action="/profile/update"]').submit();
|
||||
""")
|
||||
page.wait_for_timeout(500)
|
||||
assert page.is_visible("text=Test bio for testing")
|
||||
|
||||
@@ -99,9 +100,10 @@ def test_profile_edit_bio(alice):
|
||||
def test_profile_edit_location(alice):
|
||||
page, user = alice
|
||||
page.goto(f"{BASE_URL}/profile/{user['username']}")
|
||||
page.evaluate("document.getElementById('input-location').style.display = 'block'")
|
||||
page.locator("input[name='location']").fill("Amsterdam", force=True)
|
||||
page.click("button:has-text('Save Changes')")
|
||||
page.evaluate("""
|
||||
document.querySelector('input[name="location"]').value = 'Amsterdam';
|
||||
document.querySelector('form[action="/profile/update"]').submit();
|
||||
""")
|
||||
page.wait_for_timeout(500)
|
||||
assert page.is_visible("text=Amsterdam")
|
||||
|
||||
@@ -110,16 +112,12 @@ def test_profile_edit_all_fields(alice):
|
||||
page, user = alice
|
||||
page.goto(f"{BASE_URL}/profile/{user['username']}")
|
||||
page.evaluate("""
|
||||
document.getElementById('input-bio').style.display = 'block';
|
||||
document.getElementById('input-location').style.display = 'block';
|
||||
document.getElementById('input-git_link').style.display = 'block';
|
||||
document.getElementById('input-website').style.display = 'block';
|
||||
document.querySelector('textarea[name="bio"]').value = 'Full stack dev';
|
||||
document.querySelector('input[name="location"]').value = 'London, UK';
|
||||
document.querySelector('input[name="git_link"]').value = 'https://git.example.com/alice';
|
||||
document.querySelector('input[name="website"]').value = 'https://alice.example.com';
|
||||
document.querySelector('form[action="/profile/update"]').submit();
|
||||
""")
|
||||
page.locator("textarea[name='bio']").fill("Full stack dev", force=True)
|
||||
page.locator("input[name='location']").fill("London, UK", force=True)
|
||||
page.locator("input[name='git_link']").fill("https://git.example.com/alice", force=True)
|
||||
page.locator("input[name='website']").fill("https://alice.example.com", force=True)
|
||||
page.click("button:has-text('Save Changes')")
|
||||
page.wait_for_timeout(500)
|
||||
assert page.is_visible("text=Full stack dev")
|
||||
assert page.is_visible("text=London, UK")
|
||||
|
||||
Reference in New Issue
Block a user