from tests.conftest import BASE_URL, assert_share_copies def create_post(page, topic="random", content="Test post content", title=None): page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded") page.locator(".feed-fab").first.wait_for(state="visible", timeout=10000) page.locator(".feed-fab").first.click() page.check(f"input[value='{topic}']") page.fill("#post-content", content) if title: page.fill("#post-title", title) page.locator("#create-post-modal button.btn-primary:has-text('Post')").click() page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded") def test_post_detail_page(alice): page, _ = alice create_post(page, "random", "Detail page test content") assert page.is_visible("text=Detail page test content") assert page.is_visible("text=Back to Feed") def test_post_topic_badge(alice): page, _ = alice create_post(page, "question", "Question post for badge check") badge = page.locator(".badge-question") assert badge.is_visible() def test_post_author_info(alice): page, user = alice create_post(page, "fun", "Post with author check") assert page.is_visible(f"text={user['username']}") def test_post_vote_button(alice): page, _ = alice create_post(page, "devlog", "Votable post") vote_btn = page.locator(".post-action-btn.vote-up").first assert vote_btn.is_visible() def test_post_vote_increment(alice): page, _ = alice create_post(page, "showcase", "Vote increment test") page.locator(".post-action-btn.vote-up").first.click() page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded") count = page.locator(".post-vote-count").first.text_content().strip() assert count == "1", f"expected vote count 1, got {count!r}" def test_post_comments_section(alice): page, _ = alice create_post(page, "rant", "Comment section test") assert page.is_visible("text=Comments") assert page.is_visible("textarea[placeholder='Your opinion goes here...']") def test_add_comment(alice): page, _ = alice create_post(page, "random", "Post for commenting") textarea = page.locator(".comment-form textarea[name='content']") textarea.fill("This is a test comment from Playwright") 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") def test_comment_voting(alice): page, _ = alice create_post(page, "devlog", "Post for comment voting") textarea = page.locator(".comment-form textarea[name='content']") textarea.fill("Votable comment") 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 upvote.click() page.wait_for_timeout(500) def test_delete_own_comment(alice): page, _ = alice create_post(page, "fun", "Post for comment deletion") textarea = page.locator(".comment-form textarea[name='content']") textarea.fill("Comment to delete") 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(): delete_btn.click() page.wait_for_timeout(500) def test_comment_form_elements(alice): page, _ = alice create_post(page, "random", "Post for checking comment form") assert page.is_visible("textarea[placeholder='Your opinion goes here...']") def test_share_button(alice): page, _ = alice create_post(page, "showcase", "Share button check") assert_share_copies(page, "/posts/") def test_back_to_feed_link(alice): page, _ = alice create_post(page, "random", "Back link test") back = page.locator("a:has-text('Back to Feed')") assert back.is_visible() back.click() page.wait_for_url(f"{BASE_URL}/feed", wait_until="domcontentloaded") def test_multiple_comments_on_post(alice): page, _ = alice create_post(page, "devlog", "Post with many comments") for i in range(3): 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") def test_comment_and_vote_then_delete(alice): page, _ = alice create_post(page, "showcase", "Full comment lifecycle 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") vote_up = page.locator(".comment-vote-btn").first vote_up.click() page.wait_for_timeout(300) delete_btn = page.locator(".comment-action-btn:has-text('Delete')") if delete_btn.is_visible(): delete_btn.click() 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", "signals"] for topic in topics: create_post(page, topic, f"Topic test post for {topic}") badge = page.locator(f".badge-{topic}") assert badge.is_visible() page.wait_for_timeout(200) def test_delete_own_post(alice): page, _ = alice create_post(page, "random", "Post to be deleted permanently here") post_url = page.url page.once("dialog", lambda d: d.accept()) page.locator(".post-action-btn:has-text('Delete')").click() page.wait_for_url(f"{BASE_URL}/feed", wait_until="domcontentloaded") resp = page.goto(post_url, wait_until="domcontentloaded") assert resp.status == 404 def test_content_rendering_markdown_and_highlight(alice): page, _ = alice create_post(page, "random", "Hello **world bold** text\n\n```python\nprint('hi')\n```") page.locator(".rendered-content strong").first.wait_for(state="visible") assert page.locator(".rendered-content pre code").count() >= 1 def test_mention_autocomplete(alice): page, _ = alice create_post(page, "random", "Post for mention autocomplete here") ta = page.locator(".comment-form textarea[data-mention]").first ta.click() ta.press_sequentially("@bob") item = page.locator(".mention-dropdown-item").first item.wait_for(state="visible") assert "bob" in item.inner_text().lower() def test_emoji_picker_opens(alice): page, _ = alice create_post(page, "random", "Post for emoji picker here") page.locator(".emoji-toggle-btn").first.click() page.locator(".emoji-picker-wrapper").first.wait_for(state="visible") assert page.locator("emoji-picker").first.is_visible() def test_attachment_upload_ui(alice): import io from PIL import Image page, _ = alice create_post(page, "random", "Post for attachment upload UI") buf = io.BytesIO() Image.new("RGB", (4, 4), (0, 128, 255)).save(buf, "PNG") file_input = page.locator(".comment-form .attachment-upload-container input[type='file']").first file_input.set_input_files({"name": "pic.png", "mimeType": "image/png", "buffer": buf.getvalue()}) page.locator(".attachment-preview[data-uid]").first.wait_for(state="visible", timeout=15000)