"""E2E tests for editing and deleting posts. Tests post modification and deletion flows with permission checks. """ import pytest from playwright.sync_api import Page @pytest.mark.e2e class TestPostEditing: """Tests for editing posts.""" def test_edit_button_visible_for_owner(self, page: Page, base_url: str) -> None: """Test edit button visible for post owner.""" page.goto(f"{base_url}/web/") page.wait_for_load_state("networkidle") # Check if empty state empty_state = page.locator("[data-testid='empty-state']") if empty_state.is_visible(): pytest.skip("No posts available") # Navigate to first post read_more = page.locator("[data-testid^='btn-read-more-']").first if not read_more.is_visible(): pytest.skip("No posts to click") read_more.click() page.wait_for_load_state("networkidle") # Check if edit button is visible edit_btn = page.locator("[data-testid='btn-edit-post']") if edit_btn.is_visible(): assert edit_btn.is_visible() else: pytest.skip("User cannot edit this post") def test_edit_form_loads(self, page: Page, base_url: str) -> None: """Test that edit form loads with post data.""" page.goto(f"{base_url}/web/") page.wait_for_load_state("networkidle") empty_state = page.locator("[data-testid='empty-state']") if empty_state.is_visible(): pytest.skip("No posts available") read_more = page.locator("[data-testid^='btn-read-more-']").first if not read_more.is_visible(): pytest.skip("No posts to click") read_more.click() page.wait_for_load_state("networkidle") edit_btn = page.locator("[data-testid='btn-edit-post']") if not edit_btn.is_visible(): pytest.skip("Edit not available") edit_btn.click() page.wait_for_load_state("networkidle") # Check form loaded form = page.locator("[data-testid='form-post']") assert form.is_visible(), "Form should be visible" def test_edit_post_title(self, authenticated_page: Page, base_url: str) -> None: """Test editing post title.""" page = authenticated_page page.goto(f"{base_url}/web/") page.wait_for_load_state("networkidle") empty_state = page.locator("[data-testid='empty-state']") if empty_state.is_visible(): pytest.skip("No posts available") read_more = page.locator("[data-testid^='btn-read-more-']").first if not read_more.is_visible(): pytest.skip("No posts to click") read_more.click() page.wait_for_load_state("networkidle") edit_btn = page.locator("[data-testid='btn-edit-post']") if not edit_btn.is_visible(): pytest.skip("Edit not available") # Get current title original_title = page.locator("[data-testid='post-detail-title']").text_content() or "" edit_btn.click() page.wait_for_load_state("networkidle") new_title = f"{original_title} (Edited)" page.locator("[data-testid='input-title']").fill(new_title) page.locator("[data-testid='btn-submit-post']").click() page.wait_for_load_state("networkidle") # Verify title changed updated_title = page.locator("[data-testid='post-detail-title']").text_content() assert updated_title == new_title @pytest.mark.e2e class TestPostDeletion: """Tests for deleting posts.""" def test_delete_button_visible_for_owner(self, page: Page, base_url: str) -> None: """Test delete button visible for post owner.""" page.goto(f"{base_url}/web/") page.wait_for_load_state("networkidle") empty_state = page.locator("[data-testid='empty-state']") if empty_state.is_visible(): pytest.skip("No posts available") read_more = page.locator("[data-testid^='btn-read-more-']").first if not read_more.is_visible(): pytest.skip("No posts to click") read_more.click() page.wait_for_load_state("networkidle") delete_btn = page.locator("[data-testid='btn-delete-post']") if delete_btn.is_visible(): assert delete_btn.is_visible() else: pytest.skip("User cannot delete this post") def test_delete_shows_confirmation(self, page: Page, base_url: str) -> None: """Test delete shows confirmation modal.""" page.goto(f"{base_url}/web/") page.wait_for_load_state("networkidle") empty_state = page.locator("[data-testid='empty-state']") if empty_state.is_visible(): pytest.skip("No posts available") read_more = page.locator("[data-testid^='btn-read-more-']").first if not read_more.is_visible(): pytest.skip("No posts to click") read_more.click() page.wait_for_load_state("networkidle") delete_btn = page.locator("[data-testid='btn-delete-post']") if not delete_btn.is_visible(): pytest.skip("Delete not available") delete_btn.click() page.wait_for_load_state("networkidle") # Confirmation modal should appear confirm_btn = page.locator("[data-testid='btn-confirm-delete']") cancel_btn = page.locator("[data-testid='btn-cancel-delete']") assert confirm_btn.is_visible() assert cancel_btn.is_visible() def test_cancel_delete_keeps_post(self, page: Page, base_url: str) -> None: """Test canceling deletion keeps the post.""" page.goto(f"{base_url}/web/") page.wait_for_load_state("networkidle") empty_state = page.locator("[data-testid='empty-state']") if empty_state.is_visible(): pytest.skip("No posts available") read_more = page.locator("[data-testid^='btn-read-more-']").first if not read_more.is_visible(): pytest.skip("No posts to click") read_more.click() page.wait_for_load_state("networkidle") original_title = page.locator("[data-testid='post-detail-title']").text_content() or "" delete_btn = page.locator("[data-testid='btn-delete-post']") if not delete_btn.is_visible(): pytest.skip("Delete not available") delete_btn.click() page.wait_for_load_state("networkidle") # Click cancel page.locator("[data-testid='btn-cancel-delete']").click() page.wait_for_load_state("networkidle") # Should still be on detail page with same post current_title = page.locator("[data-testid='post-detail-title']").text_content() assert current_title == original_title @pytest.mark.e2e class TestPublishUnpublish: """Tests for publishing and unpublishing posts.""" def test_publish_button_for_draft(self, page: Page, base_url: str) -> None: """Test publish button visible for draft posts.""" page.goto(f"{base_url}/web/") page.wait_for_load_state("networkidle") empty_state = page.locator("[data-testid='empty-state']") if empty_state.is_visible(): pytest.skip("No posts available") # Try to find a draft post posts = page.locator("[data-testid^='post-card-']") count = posts.count() if count == 0: pytest.skip("No posts available") # Click first post posts.first.click() page.wait_for_load_state("networkidle") # Check status status = page.locator("[data-testid='post-detail-status']").text_content() or "" if "draft" in status.lower(): publish_btn = page.locator("[data-testid='btn-publish-post']") if publish_btn.is_visible(): assert publish_btn.is_visible() else: pytest.skip("Not a draft post") def test_unpublish_button_for_published(self, page: Page, base_url: str) -> None: """Test unpublish button visible for published posts.""" page.goto(f"{base_url}/web/") page.wait_for_load_state("networkidle") empty_state = page.locator("[data-testid='empty-state']") if empty_state.is_visible(): pytest.skip("No posts available") posts = page.locator("[data-testid^='post-card-']") if posts.count() == 0: pytest.skip("No posts available") # Click first post posts.first.click() page.wait_for_load_state("networkidle") # Check status status = page.locator("[data-testid='post-detail-status']").text_content() or "" if "published" in status.lower(): unpublish_btn = page.locator("[data-testid='btn-unpublish-post']") if unpublish_btn.is_visible(): assert unpublish_btn.is_visible() else: pytest.skip("Not a published post") @pytest.mark.e2e class TestPermissions: """Tests for edit/delete permissions.""" def test_cannot_edit_other_users_post(self, page: Page, base_url: str) -> None: """Test user cannot edit another user's post.""" page.goto(f"{base_url}/web/") page.wait_for_load_state("networkidle") # Check if logged in logout_link = page.locator("[data-testid='nav-link-logout']") if not logout_link.is_visible(): pytest.skip("Requires authenticated user") empty_state = page.locator("[data-testid='empty-state']") if empty_state.is_visible(): pytest.skip("No posts available") posts = page.locator("[data-testid^='post-card-']") if posts.count() == 0: pytest.skip("No posts available") posts.first.click() page.wait_for_load_state("networkidle") # If edit button is not visible, user cannot edit edit_btn = page.locator("[data-testid='btn-edit-post']") if not edit_btn.is_visible(): pass # Test passes - user cannot edit def test_guest_cannot_see_edit_delete(self, page: Page, base_url: str) -> None: """Test guest user cannot see edit/delete buttons.""" page.goto(f"{base_url}/web/") page.wait_for_load_state("networkidle") # Check if guest (login link visible) login_link = page.locator("a[href='/auth/login']") if not login_link.is_visible(): pytest.skip("Requires guest user") empty_state = page.locator("[data-testid='empty-state']") if empty_state.is_visible(): pytest.skip("No posts available") posts = page.locator("[data-testid^='post-card-']") if posts.count() == 0: pytest.skip("No posts available") posts.first.click() page.wait_for_load_state("networkidle") edit_btn = page.locator("[data-testid='btn-edit-post']") delete_btn = page.locator("[data-testid='btn-delete-post']") assert not edit_btn.is_visible(), "Edit button should be hidden for guests" assert not delete_btn.is_visible(), "Delete button should be hidden for guests"