Files
blog.pyaqa.ru/tests/e2e/test_post_lifecycle.py
Sergey Vanyushkin cf4982c0e5 test(e2e): add TC-E2E-003/004/005/007/008/009/010 — delete, pagination, errors, profile, theme
- test_post_deletion.py: user delete own, admin delete any, 403 for other's
- test_pagination.py: navigation across pages, boundary on last page
- test_errors.py: 404 nonexistent post, 404 for other user's draft
- test_post_lifecycle.py: draft-to-publish via edit flow
- test_post_ownership.py: user can edit own post
- test_profile_and_theme.py: profile page rendering, theme toggle with localStorage
- fix(web): remove infinite pagination for USER role (routes.py)
- fix(e2e): stabilize all publish() calls with expect_navigation
- fix(e2e): add _unique_title() to avoid slug collisions at scale
- docs: update FEATURE_POST_LIFECYCLE.md and TEST_MODEL.md coverage
2026-05-08 20:25:01 +03:00

253 lines
8.7 KiB
Python

"""End-to-end tests for blog post lifecycle.
Tests the complete flow from post creation through publishing
and visibility verification across different user roles.
"""
from __future__ import annotations
import uuid
import pytest
from playwright.sync_api import Page
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_user_creates_and_publishes_post_visible_to_guest_and_admin(
user_page: Page,
guest_page: Page,
admin_page: Page,
base_url: str,
) -> None:
"""Test positive scenario: user creates post, publishes it, and verifies visibility.
Steps:
1. Generate unique post data.
2. Authenticated user opens home, clicks "Write a Post".
3. Fills form and publishes the post.
4. Verifies redirect to detail page with "Published" status.
5. Verifies post appears on home page for the user.
6. Verifies post is visible to guest (unauthenticated).
7. Verifies post detail is accessible to guest.
8. Verifies post is visible to admin.
9. Verifies post detail is accessible to admin.
Args:
user_page: Playwright page authenticated as regular user.
guest_page: Unauthenticated Playwright page.
admin_page: Playwright page authenticated as admin.
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
assert detail.is_published()
home.open()
assert home.has_post_with_title(title)
guest_home = HomePage(guest_page, base_url)
guest_home.open()
assert guest_home.has_post_with_title(title)
guest_detail = PostDetailPage(guest_page, base_url, slug)
guest_detail.open()
assert guest_detail.get_title() == title
assert guest_detail.is_published()
admin_home = HomePage(admin_page, base_url)
admin_home.open()
assert admin_home.has_post_with_title(title)
admin_detail = PostDetailPage(admin_page, base_url, slug)
admin_detail.open()
assert admin_detail.get_title() == title
assert admin_detail.is_published()
@pytest.mark.e2e
def test_post_visibility_policies_across_users(
user_page: Page,
user2_page: Page,
guest_page: Page,
admin_page: Page,
base_url: str,
) -> None:
"""Test visibility policies: drafts vs published posts across roles.
Steps:
1. User creates a draft post.
2. User creates and publishes another post.
3. Verify user sees both posts on the home page.
4. Verify user2 sees only the published post.
5. Verify guest sees only the published post.
6. Verify admin sees both posts.
7. Verify user2 receives 404 when accessing the draft directly.
Args:
user_page: Playwright page authenticated as the first regular user.
user2_page: Playwright page authenticated as the second regular user.
guest_page: Unauthenticated Playwright page.
admin_page: Playwright page authenticated as admin.
base_url: Application base URL.
"""
generator = PostDataGenerator()
draft_data = generator.generate_post()
draft_title = _unique_title(str(draft_data["title"]))
draft_content = str(draft_data["content"])
draft_tags = ", ".join(draft_data["tags"])
home = HomePage(user_page, base_url)
home.open()
home.create_post()
form = PostFormPage(user_page, base_url)
form.fill_form(draft_title, draft_content, draft_tags)
with user_page.expect_navigation(wait_until="networkidle"):
form.save_draft()
draft_url = user_page.url
assert "new" not in draft_url, f"Still on form page: {draft_url}"
draft_slug = draft_url.rstrip("/").split("/")[-1]
user_page.wait_for_selector('[data-testid="post-detail-title"]')
draft_detail = PostDetailPage(user_page, base_url, draft_slug)
assert draft_detail.get_title() == draft_title
assert not draft_detail.is_published()
published_data = generator.generate_post()
published_title = _unique_title(str(published_data["title"]))
published_content = str(published_data["content"])
published_tags = ", ".join(published_data["tags"])
home.open()
home.create_post()
form = PostFormPage(user_page, base_url)
form.fill_form(published_title, published_content, published_tags)
with user_page.expect_navigation(wait_until="networkidle"):
form.publish()
published_url = user_page.url
assert "new" not in published_url, f"Still on form page: {published_url}"
published_slug = published_url.rstrip("/").split("/")[-1]
user_page.wait_for_selector('[data-testid="post-detail-title"]')
published_detail = PostDetailPage(user_page, base_url, published_slug)
assert published_detail.get_title() == published_title
assert published_detail.is_published()
home.open()
assert home.has_post_with_title(draft_title)
assert home.has_post_with_title(published_title)
user2_home = HomePage(user2_page, base_url)
user2_home.open()
assert user2_home.has_no_post_with_title(draft_title)
assert user2_home.has_post_with_title(published_title)
guest_home = HomePage(guest_page, base_url)
guest_home.open()
assert guest_home.has_no_post_with_title(draft_title)
assert guest_home.has_post_with_title(published_title)
admin_home = HomePage(admin_page, base_url)
admin_home.open()
assert admin_home.has_post_with_title(draft_title)
assert admin_home.has_post_with_title(published_title)
user2_page.goto(f"{base_url}/web/posts/{draft_slug}")
user2_page.wait_for_selector('[data-testid="error-code"]', timeout=10000)
error_code = user2_page.locator('[data-testid="error-code"]').text_content()
assert error_code == "404"
@pytest.mark.e2e
def test_user_saves_draft_then_publishes_via_edit(
user_page: Page,
guest_page: Page,
base_url: str,
) -> None:
"""Test draft-to-publish flow through the web UI.
Steps:
1. User creates a post and saves it as draft.
2. Verify the post shows Draft status and guest cannot see it.
3. User opens the edit form and clicks Update Post (publish).
4. Verify the post shows Published status and guest can now see it.
Args:
user_page: Playwright page authenticated as regular user.
guest_page: Unauthenticated Playwright page.
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.save_draft()
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
assert detail.get_status() == "Draft"
guest_home = HomePage(guest_page, base_url)
guest_home.open()
assert guest_home.has_no_post_with_title(title)
detail.edit()
user_page.wait_for_url(
lambda url: f"/web/posts/{slug}/edit" in url,
timeout=15000,
)
edit_form = PostFormPage(user_page, base_url)
with user_page.expect_navigation(wait_until="networkidle"):
edit_form.publish()
user_page.wait_for_selector('[data-testid="post-detail-title"]')
updated_detail = PostDetailPage(user_page, base_url, slug)
assert updated_detail.get_title() == title
assert updated_detail.get_status() == "Published"
guest_home.open()
assert guest_home.has_post_with_title(title)