This commit is contained in:
2026-05-11 07:02:06 +02:00
parent def6a08c0c
commit f61b1a4762
40 changed files with 1554 additions and 49 deletions
+55
View File
@@ -0,0 +1,55 @@
from tests.conftest import BASE_URL
def test_bugs_page_loads(alice):
page, _ = alice
page.goto(f"{BASE_URL}/bugs", wait_until="domcontentloaded")
assert page.is_visible("h1:has-text('Bug Reports')")
def test_bugs_create_modal(alice):
page, _ = alice
page.goto(f"{BASE_URL}/bugs", wait_until="domcontentloaded")
page.click("button:has-text('Report Bug')")
assert page.is_visible("h3:has-text('Report a Bug')")
def test_bugs_create_submit(alice):
page, _ = alice
page.goto(f"{BASE_URL}/bugs", wait_until="domcontentloaded")
page.click("button:has-text('Report Bug')")
page.fill("#bug-title", "Test bug report")
page.fill("#bug-description", "This is a test bug description")
page.click("button:has-text('Submit Report')")
page.wait_for_timeout(500)
assert page.is_visible("text=Test bug report")
def test_bugs_empty_state(alice):
page, _ = alice
page.goto(f"{BASE_URL}/bugs", wait_until="domcontentloaded")
assert page.is_visible("text=No bug reports yet.")
def test_bugs_footer_link(alice):
page, _ = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
link = page.locator("a:has-text('Bug Report')")
assert link.is_visible()
link.click()
page.wait_for_url(f"{BASE_URL}/bugs", wait_until="domcontentloaded")
def test_bugs_create_cancel(alice):
page, _ = alice
page.goto(f"{BASE_URL}/bugs", wait_until="domcontentloaded")
page.click("button:has-text('Report Bug')")
page.click("button:has-text('Cancel')")
modal = page.locator("#create-bug-modal")
assert not modal.is_visible()
def test_bugs_unauth_empty(alice):
page, _ = alice
page.goto(f"{BASE_URL}/bugs", wait_until="domcontentloaded")
assert page.is_visible("h1:has-text('Bug Reports')")
+1 -1
View File
@@ -10,7 +10,7 @@ def create_post(page, content, topic="random", title=None):
page.fill("#post-content", content)
if title:
page.fill("#post-title", title)
page.click("button:has-text('Post')")
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url("**/posts/*", timeout=10000, wait_until="domcontentloaded")
+37 -5
View File
@@ -72,7 +72,7 @@ def test_create_post_fab(alice):
fab.click()
assert page.is_visible("text=Create New Post")
assert page.is_visible("#post-content")
assert page.is_visible("button:has-text('Post')")
assert page.is_visible("#create-post-modal button:has-text('Post')")
def test_create_post_devlog(alice):
@@ -82,7 +82,7 @@ def test_create_post_devlog(alice):
page.check("input[value='devlog']")
page.fill("#post-content", "This is a test devlog post created by Playwright")
page.fill("#post-title", "Test Devlog Title")
page.click("button:has-text('Post')")
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
assert page.is_visible("text=Test Devlog Title")
assert page.is_visible("text=This is a test devlog post created by Playwright")
@@ -94,7 +94,7 @@ def test_create_post_showcase(alice):
page.locator(".feed-fab").click()
page.check("input[value='showcase']")
page.fill("#post-content", "Check out my new project showcase")
page.click("button:has-text('Post')")
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
@@ -104,7 +104,7 @@ def test_create_post_without_title(alice):
page.locator(".feed-fab").click()
page.check("input[value='rant']")
page.fill("#post-content", "Rant without a title")
page.click("button:has-text('Post')")
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
assert page.is_visible("text=Rant without a title")
@@ -131,11 +131,43 @@ def test_topnav_user_menu(alice):
assert page.is_visible(f"text={user['username']}")
def test_feed_inline_comment(alice):
page, _ = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".feed-fab").click()
page.check("input[value='devlog']")
page.fill("#post-content", "Post with inline comment test " + "x" * 30)
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
inline_form = page.locator(".feed-comment-form").first
if inline_form.is_visible():
inline_form.locator("input[name='content']").fill("Inline comment from feed")
inline_form.locator(".feed-comment-submit").click()
page.wait_for_timeout(500)
def test_feed_inline_comment_placeholder(alice):
page, _ = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
inline_input = page.locator(".feed-comment-form input").first
if inline_input.is_visible():
placeholder = inline_input.get_attribute("placeholder")
assert placeholder == "Your opinion goes here..."
def test_feed_signals_topic(alice):
page, _ = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
signals_link = page.locator("a:has-text('Signals')")
assert signals_link.is_visible()
def test_create_post_cancel_modal(alice):
page, _ = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".feed-fab").click()
cancel = page.locator("button:has-text('Cancel')").first
cancel = page.locator("#create-post-modal button:has-text('Cancel')").first
cancel.click()
modal = page.locator("#create-post-modal")
assert not modal.is_visible()
+52 -12
View File
@@ -9,7 +9,7 @@ def create_post(page, topic="random", content="Test post content", title=None):
page.fill("#post-content", content)
if title:
page.fill("#post-title", title)
page.click("button:has-text('Post')")
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
@@ -60,9 +60,9 @@ def test_post_comments_section(alice):
def test_add_comment(alice):
page, _ = alice
create_post(page, "random", "Post for commenting")
textarea = page.locator("textarea[name='content']")
textarea = page.locator(".comment-form textarea[name='content']")
textarea.fill("This is a test comment from Playwright")
page.click("button:has-text('Post')")
page.locator(".comment-form button:has-text('Post')").click()
page.wait_for_timeout(500)
assert page.is_visible("text=This is a test comment from Playwright")
@@ -70,9 +70,9 @@ def test_add_comment(alice):
def test_comment_voting(alice):
page, _ = alice
create_post(page, "devlog", "Post for comment voting")
textarea = page.locator("textarea[name='content']")
textarea = page.locator(".comment-form textarea[name='content']")
textarea.fill("Votable comment")
page.click("button:has-text('Post')")
page.locator(".comment-form button:has-text('Post')").click()
page.wait_for_timeout(500)
vote_btns = page.locator(".comment-vote-btn")
upvote = vote_btns.first
@@ -83,9 +83,9 @@ def test_comment_voting(alice):
def test_delete_own_comment(alice):
page, _ = alice
create_post(page, "fun", "Post for comment deletion")
textarea = page.locator("textarea[name='content']")
textarea = page.locator(".comment-form textarea[name='content']")
textarea.fill("Comment to delete")
page.click("button:has-text('Post')")
page.locator(".comment-form button:has-text('Post')").click()
page.wait_for_timeout(500)
delete_btn = page.locator(".comment-action-btn:has-text('Delete')").last
if delete_btn.is_visible():
@@ -119,8 +119,8 @@ def test_multiple_comments_on_post(alice):
page, _ = alice
create_post(page, "devlog", "Post with many comments")
for i in range(3):
page.locator("textarea[name='content']").fill(f"Comment number {i + 1}")
page.click("button:has-text('Post')")
page.locator(".comment-form textarea[name='content']").fill(f"Comment number {i + 1}")
page.locator(".comment-form button:has-text('Post')").click()
page.wait_for_timeout(300)
assert page.is_visible("text=Comment number 1")
assert page.is_visible("text=Comment number 3")
@@ -129,8 +129,8 @@ def test_multiple_comments_on_post(alice):
def test_comment_and_vote_then_delete(alice):
page, _ = alice
create_post(page, "showcase", "Full comment lifecycle post")
page.locator("textarea[name='content']").fill("Lifecycle comment")
page.click("button:has-text('Post')")
page.locator(".comment-form textarea[name='content']").fill("Lifecycle comment")
page.locator(".comment-form button:has-text('Post')").click()
page.wait_for_timeout(300)
assert page.is_visible("text=Lifecycle comment")
@@ -144,9 +144,49 @@ def test_comment_and_vote_then_delete(alice):
page.wait_for_timeout(300)
def test_post_edit_button(alice):
page, _ = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".feed-fab").click()
page.fill("#post-content", "Post to be edited later")
page.fill("#post-title", "Editable Post")
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
edit_btn = page.locator("button:has-text('Edit')")
assert edit_btn.is_visible()
def test_post_edit_modal(alice):
page, _ = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".feed-fab").click()
page.fill("#post-content", "Post for edit modal test")
page.fill("#post-title", "Edit Modal Test")
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
page.click("button:has-text('Edit')")
assert page.is_visible("h3:has-text('Edit Post')")
def test_post_edit_submit(alice):
page, _ = alice
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".feed-fab").click()
page.fill("#post-content", "Original content for editing test")
page.fill("#post-title", "Original Title")
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
page.click("button:has-text('Edit')")
page.fill("#edit-title", "Edited Title")
page.fill("#edit-content", "Edited content for the post")
page.click("button:has-text('Save Changes')")
page.wait_for_timeout(500)
assert page.is_visible("text=Edited Title")
def test_post_across_all_topics(alice):
page, _ = alice
topics = ["devlog", "showcase", "question", "rant", "fun"]
topics = ["devlog", "showcase", "question", "rant", "fun", "signals"]
for topic in topics:
create_post(page, topic, f"Topic test post for {topic}")
badge = page.locator(f".badge-{topic}")
+14
View File
@@ -131,6 +131,20 @@ def test_profile_viewing_other_user(bob, alice):
assert page_b.is_visible("text=alice_test")
def test_profile_message_button(bob, alice):
page_b, _ = bob
page_b.goto(f"{BASE_URL}/profile/alice_test", wait_until="domcontentloaded")
message_btn = page_b.locator("a:has-text('Send Message')")
assert message_btn.is_visible()
def test_profile_message_button_self(alice):
page, user = alice
page.goto(f"{BASE_URL}/profile/{user['username']}", wait_until="domcontentloaded")
message_btn = page.locator("a:has-text('Send Message')")
assert not message_btn.is_visible()
def test_profile_activity_tab(alice):
page, user = alice
page.goto(f"{BASE_URL}/profile/{user['username']}?tab=activity", wait_until="domcontentloaded")
+36
View File
@@ -126,3 +126,39 @@ def test_projects_count(alice):
count_text = page.text_content(".projects-count")
assert "Showing" in count_text
assert "projects" in count_text
def test_project_detail_page(alice):
page, _ = alice
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
page.locator("#create-project-btn").click()
page.fill("#title", "DetailTestProject")
page.fill("#description", "Project detail page test")
page.click("button:has-text('Create Project')")
page.wait_for_timeout(500)
card = page.locator("a.project-card:has-text('DetailTestProject')").first
card.click()
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
assert page.is_visible("text=DetailTestProject")
assert page.is_visible("text=Back to Projects")
def test_project_detail_unauth(page):
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
def test_project_detail_back_link(alice):
page, _ = alice
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
page.locator("#create-project-btn").click()
page.fill("#title", "BackLinkProject")
page.fill("#description", "Test back link")
page.click("button:has-text('Create Project')")
page.wait_for_timeout(500)
card = page.locator("a.project-card:has-text('BackLinkProject')").first
card.click()
page.wait_for_timeout(500)
back = page.locator("a:has-text('Back to Projects')")
assert back.is_visible()
back.click()
page.wait_for_url(f"{BASE_URL}/projects", wait_until="domcontentloaded")
+1 -1
View File
@@ -101,7 +101,7 @@ def test_post_page_has_structured_data(page, app_server):
page.locator(".feed-fab").click()
page.fill("#post-content", "This is a post for SEO testing with structured data schema validation")
page.fill("#post-title", "SEO Post For Schema")
page.click("button:has-text('Post')")
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", timeout=10000, wait_until="domcontentloaded")
scripts = page.locator('script[type="application/ld+json"]')
count = scripts.count()
+55
View File
@@ -0,0 +1,55 @@
from tests.conftest import BASE_URL
from devplacepy.database import get_table
def _promote_to_admin(username: str) -> None:
users = get_table("users")
user = users.find_one(username=username)
if user:
users.update({"uid": user["uid"], "role": "Admin"}, ["uid"])
def test_services_page_non_admin_redirect(alice):
page, _ = alice
page.goto(f"{BASE_URL}/admin/services", wait_until="domcontentloaded")
assert "/feed" in page.url or "/" in page.url
def test_services_page_admin_loads(alice):
page, user = alice
_promote_to_admin(user["username"])
page.goto(f"{BASE_URL}/admin/services", wait_until="domcontentloaded")
assert page.is_visible("text=Services")
def test_services_data_json(alice):
page, user = alice
_promote_to_admin(user["username"])
page.goto(f"{BASE_URL}/admin/services/data", wait_until="domcontentloaded")
import json
text = page.inner_text("pre") if page.locator("pre").is_visible() else page.content()
assert "news" in text
def test_services_sidebar_link(alice):
page, user = alice
_promote_to_admin(user["username"])
page.goto(f"{BASE_URL}/admin/services", wait_until="domcontentloaded")
link = page.locator(f"a[href='/admin/services']")
assert link.is_visible()
assert "active" in (link.get_attribute("class") or "")
def test_services_status_display(alice):
page, user = alice
_promote_to_admin(user["username"])
page.goto(f"{BASE_URL}/admin/services", wait_until="domcontentloaded")
assert page.is_visible(".service-status")
def test_services_log_display(alice):
page, user = alice
_promote_to_admin(user["username"])
page.goto(f"{BASE_URL}/admin/services", wait_until="domcontentloaded")
log = page.locator(".service-log")
assert log.is_visible()