"""End-to-end tests for blog post comments. Tests comment creation, nested replies with @username prefix, and comment visibility for authenticated users. """ from __future__ import annotations import uuid import pytest from playwright.sync_api import Page, expect from pytfm.generators import PostDataGenerator from tests.e2e.pages import HomePage, PostDetailPage, PostFormPage def _unique_title(base: str) -> str: """Append a short UUID to a title to avoid slug collisions.""" return f"{base} {uuid.uuid4().hex[:8]}" @pytest.mark.e2e def test_create_and_reply_comment( user_page: Page, base_url: str, ) -> None: """Test TC-E2E-110: Create a top-level comment, reply, and nested reply. Steps: 1. User creates and publishes a post. 2. User navigates to the post detail page. 3. User clicks "Write a Comment" button. 4. User types a top-level comment and submits it. 5. Page reloads, top-level comment is visible. 6. User clicks "Reply" on the top-level comment. 7. User types a reply and submits it. 8. Page reloads, reply is visible under the parent comment. 9. User clicks "Reply" on the reply (nested reply). 10. User types a nested reply and submits it. 11. Page reloads, nested reply is visible under the reply. Args: user_page: Playwright page authenticated as regular user. base_url: Application base URL. """ generator = PostDataGenerator() post_data = generator.generate_post() title = _unique_title(str(post_data["title"])) content = str(post_data["content"]) tags = ", ".join(post_data["tags"]) home = HomePage(user_page, base_url) home.open() home.create_post() form = PostFormPage(user_page, base_url) form.fill_form(title, content, tags) with user_page.expect_navigation(wait_until="networkidle"): form.publish() current_url = user_page.url assert "new" not in current_url, f"Still on form page: {current_url}" slug = current_url.rstrip("/").split("/")[-1] user_page.wait_for_selector('[data-testid="post-detail-title"]') detail = PostDetailPage(user_page, base_url, slug) assert detail.get_title() == title # Click "Write a Comment" button to show the form user_page.locator('[data-testid="btn-show-comment-form"]').click() user_page.wait_for_selector('[data-testid="form-create-comment"]', state="visible") # Write a top-level comment comment_text = "This is a top-level comment with enough length for testing." user_page.locator('[data-testid="input-comment-content"]').fill(comment_text) # Submit the comment user_page.locator('[data-testid="submit-comment"]').click() # Page should reload after successful comment creation user_page.wait_for_selector('[data-testid="comments-section"]', timeout=15000) # Verify the top-level comment appears comment_locator = user_page.locator('[data-testid^="comment-content-"]', has_text=comment_text) expect(comment_locator.first).to_be_visible(timeout=10000) # Get the comment ID for the reply button top_comment = user_page.locator('[data-testid^="comment-"][data-comment-id]').first comment_id = top_comment.get_attribute("data-comment-id") # Click Reply on the top-level comment reply_btn = user_page.locator(f'[data-testid="btn-comment-reply-{comment_id}"]') reply_btn.click() # The comment form should appear with reply info user_page.wait_for_selector('[data-testid="comment-form-help"]', state="visible") # Write a reply reply_text = "This is a reply to the comment." user_page.locator('[data-testid="input-comment-content"]').fill(reply_text) # Submit the reply user_page.locator('[data-testid="submit-comment"]').click() # Page should reload user_page.wait_for_selector('[data-testid="comments-section"]', timeout=15000) # Verify the reply appears in the comment-replies section under the parent replies_section = user_page.locator(f'[data-testid="comment-replies-{comment_id}"]') expect(replies_section).to_be_visible(timeout=10000) # Verify reply text is visible within the replies section reply_in_section = replies_section.locator( '[data-testid^="comment-content-"]', has_text=reply_text ) expect(reply_in_section).to_be_visible(timeout=5000) # Get the reply's comment ID for the nested reply reply_element = replies_section.locator('[data-testid^="comment-"][data-comment-id]').first reply_id = reply_element.get_attribute("data-comment-id") # Click Reply on the reply (nested reply) user_page.locator(f'[data-testid="btn-comment-reply-{reply_id}"]').click() user_page.wait_for_selector('[data-testid="comment-form-help"]', state="visible") # Write a nested reply nested_text = "This is a reply to the reply." user_page.locator('[data-testid="input-comment-content"]').fill(nested_text) # Submit the nested reply user_page.locator('[data-testid="submit-comment"]').click() # Page should reload user_page.wait_for_selector('[data-testid="comments-section"]', timeout=15000) # Verify the nested reply appears in the comment-replies section under the reply nested_replies = user_page.locator(f'[data-testid="comment-replies-{reply_id}"]') expect(nested_replies).to_be_visible(timeout=10000) # Verify nested reply text is visible nested_in_section = nested_replies.locator( '[data-testid^="comment-content-"]', has_text=nested_text ) expect(nested_in_section).to_be_visible(timeout=5000) @pytest.mark.e2e def test_guest_cannot_comment( guest_page: Page, user_page: Page, base_url: str, ) -> None: """Test TC-E2E-112: Guest cannot see the comment form. Steps: 1. User creates and publishes a post. 2. Guest opens the post detail page. 3. Verify guest cannot see the "Write a Comment" button. Args: guest_page: Unauthenticated Playwright page. user_page: Playwright page authenticated as regular user. base_url: Application base URL. """ generator = PostDataGenerator() post_data = generator.generate_post() title = _unique_title(str(post_data["title"])) content = str(post_data["content"]) tags = ", ".join(post_data["tags"]) home = HomePage(user_page, base_url) home.open() home.create_post() form = PostFormPage(user_page, base_url) form.fill_form(title, content, tags) with user_page.expect_navigation(wait_until="networkidle"): form.publish() current_url = user_page.url assert "new" not in current_url, f"Still on form page: {current_url}" slug = current_url.rstrip("/").split("/")[-1] # Guest opens the published post guest_detail = PostDetailPage(guest_page, base_url, slug) guest_detail.open() guest_page.wait_for_selector('[data-testid="post-detail-title"]') # Verify guest cannot see comment form or button comment_btn = guest_page.locator('[data-testid="btn-show-comment-form"]') expect(comment_btn).to_have_count(0)