mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-23 06:51:09 -05:00
Add standardized HTMX conventions, interaction patterns, and migration guide for ThrillWiki UX
This commit is contained in:
372
backend/tests/e2e/test_review_submission.py
Normal file
372
backend/tests/e2e/test_review_submission.py
Normal file
@@ -0,0 +1,372 @@
|
||||
"""
|
||||
E2E tests for review submission and moderation flows.
|
||||
|
||||
These tests verify the complete user journey for submitting,
|
||||
editing, and moderating reviews using Playwright for browser automation.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestReviewSubmission:
|
||||
"""E2E tests for review submission flow."""
|
||||
|
||||
def test__review_form__displays_fields(self, auth_page: Page, live_server, parks_data):
|
||||
"""Test review form displays all required fields."""
|
||||
park = parks_data[0]
|
||||
auth_page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
# Find and click reviews tab or section
|
||||
reviews_tab = auth_page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
# Click write review button
|
||||
write_review = auth_page.locator(
|
||||
"button:has-text('Write Review'), a:has-text('Write Review')"
|
||||
)
|
||||
|
||||
if write_review.count() > 0:
|
||||
write_review.first.click()
|
||||
|
||||
# Verify form fields
|
||||
expect(auth_page.locator("select[name='rating'], input[name='rating']").first).to_be_visible()
|
||||
expect(auth_page.locator("input[name='title'], textarea[name='title']").first).to_be_visible()
|
||||
expect(auth_page.locator("textarea[name='content'], textarea[name='review']").first).to_be_visible()
|
||||
|
||||
def test__review_submission__valid_data__creates_review(
|
||||
self, auth_page: Page, live_server, parks_data
|
||||
):
|
||||
"""Test submitting a valid review creates it."""
|
||||
park = parks_data[0]
|
||||
auth_page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
# Navigate to reviews
|
||||
reviews_tab = auth_page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
write_review = auth_page.locator(
|
||||
"button:has-text('Write Review'), a:has-text('Write Review')"
|
||||
)
|
||||
|
||||
if write_review.count() > 0:
|
||||
write_review.first.click()
|
||||
|
||||
# Fill the form
|
||||
rating_select = auth_page.locator("select[name='rating']")
|
||||
if rating_select.count() > 0:
|
||||
rating_select.select_option("5")
|
||||
else:
|
||||
# May be radio buttons or stars
|
||||
auth_page.locator("input[name='rating'][value='5']").click()
|
||||
|
||||
auth_page.locator("input[name='title'], textarea[name='title']").first.fill(
|
||||
"E2E Test Review Title"
|
||||
)
|
||||
auth_page.locator("textarea[name='content'], textarea[name='review']").first.fill(
|
||||
"This is an E2E test review content."
|
||||
)
|
||||
|
||||
auth_page.get_by_role("button", name="Submit").click()
|
||||
|
||||
# Should show success or redirect
|
||||
auth_page.wait_for_timeout(500)
|
||||
|
||||
def test__review_submission__missing_rating__shows_error(
|
||||
self, auth_page: Page, live_server, parks_data
|
||||
):
|
||||
"""Test submitting review without rating shows error."""
|
||||
park = parks_data[0]
|
||||
auth_page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
reviews_tab = auth_page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
write_review = auth_page.locator(
|
||||
"button:has-text('Write Review'), a:has-text('Write Review')"
|
||||
)
|
||||
|
||||
if write_review.count() > 0:
|
||||
write_review.first.click()
|
||||
|
||||
# Fill only title and content, skip rating
|
||||
auth_page.locator("input[name='title'], textarea[name='title']").first.fill(
|
||||
"Missing Rating Review"
|
||||
)
|
||||
auth_page.locator("textarea[name='content'], textarea[name='review']").first.fill(
|
||||
"Review without rating"
|
||||
)
|
||||
|
||||
auth_page.get_by_role("button", name="Submit").click()
|
||||
|
||||
# Should show validation error
|
||||
error = auth_page.locator(".error, .errorlist, [role='alert']")
|
||||
expect(error.first).to_be_visible()
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestReviewDisplay:
|
||||
"""E2E tests for review display."""
|
||||
|
||||
def test__reviews_list__displays_reviews(self, page: Page, live_server, parks_data):
|
||||
"""Test reviews list displays existing reviews."""
|
||||
park = parks_data[0]
|
||||
page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
# Navigate to reviews section
|
||||
reviews_tab = page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
# Reviews should be displayed
|
||||
reviews_section = page.locator(
|
||||
"[data-testid='reviews-list'], .reviews-list, .review-item"
|
||||
)
|
||||
|
||||
if reviews_section.count() > 0:
|
||||
expect(reviews_section.first).to_be_visible()
|
||||
|
||||
def test__review__shows_rating(self, page: Page, live_server, test_review):
|
||||
"""Test review displays rating."""
|
||||
# test_review fixture creates a review
|
||||
page.goto(f"{page.url}") # Stay on current page after fixture
|
||||
|
||||
# Rating should be visible (stars, number, etc.)
|
||||
rating = page.locator(
|
||||
".rating, .stars, [data-testid='rating']"
|
||||
)
|
||||
|
||||
if rating.count() > 0:
|
||||
expect(rating.first).to_be_visible()
|
||||
|
||||
def test__review__shows_author(self, page: Page, live_server, parks_data):
|
||||
"""Test review displays author name."""
|
||||
park = parks_data[0]
|
||||
page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
reviews_tab = page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
# Author name should be visible in review
|
||||
author = page.locator(
|
||||
".review-author, .author, [data-testid='author']"
|
||||
)
|
||||
|
||||
if author.count() > 0:
|
||||
expect(author.first).to_be_visible()
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestReviewEditing:
|
||||
"""E2E tests for review editing."""
|
||||
|
||||
def test__own_review__shows_edit_button(self, auth_page: Page, live_server, test_review):
|
||||
"""Test user's own review shows edit button."""
|
||||
# Navigate to reviews after creating one
|
||||
park_url = auth_page.url
|
||||
|
||||
# Look for edit button on own review
|
||||
edit_button = auth_page.locator(
|
||||
"button:has-text('Edit'), a:has-text('Edit Review')"
|
||||
)
|
||||
|
||||
if edit_button.count() > 0:
|
||||
expect(edit_button.first).to_be_visible()
|
||||
|
||||
def test__edit_review__updates_content(self, auth_page: Page, live_server, test_review):
|
||||
"""Test editing review updates the content."""
|
||||
# Find and click edit
|
||||
edit_button = auth_page.locator(
|
||||
"button:has-text('Edit'), a:has-text('Edit Review')"
|
||||
)
|
||||
|
||||
if edit_button.count() > 0:
|
||||
edit_button.first.click()
|
||||
|
||||
# Update content
|
||||
content_field = auth_page.locator(
|
||||
"textarea[name='content'], textarea[name='review']"
|
||||
)
|
||||
content_field.first.fill("Updated review content from E2E test")
|
||||
|
||||
auth_page.get_by_role("button", name="Save").click()
|
||||
|
||||
# Should show updated content
|
||||
auth_page.wait_for_timeout(500)
|
||||
expect(auth_page.get_by_text("Updated review content")).to_be_visible()
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestReviewModeration:
|
||||
"""E2E tests for review moderation."""
|
||||
|
||||
def test__moderator__sees_moderation_actions(
|
||||
self, mod_page: Page, live_server, parks_data
|
||||
):
|
||||
"""Test moderator sees moderation actions on reviews."""
|
||||
park = parks_data[0]
|
||||
mod_page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
reviews_tab = mod_page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
# Moderator should see moderation buttons
|
||||
mod_actions = mod_page.locator(
|
||||
"button:has-text('Remove'), button:has-text('Flag'), [data-testid='mod-action']"
|
||||
)
|
||||
|
||||
if mod_actions.count() > 0:
|
||||
expect(mod_actions.first).to_be_visible()
|
||||
|
||||
def test__moderator__can_remove_review(self, mod_page: Page, live_server, parks_data):
|
||||
"""Test moderator can remove a review."""
|
||||
park = parks_data[0]
|
||||
mod_page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
reviews_tab = mod_page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
remove_button = mod_page.locator("button:has-text('Remove')")
|
||||
|
||||
if remove_button.count() > 0:
|
||||
remove_button.first.click()
|
||||
|
||||
# Confirm if dialog appears
|
||||
confirm = mod_page.locator("button:has-text('Confirm')")
|
||||
if confirm.count() > 0:
|
||||
confirm.click()
|
||||
|
||||
mod_page.wait_for_timeout(500)
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestReviewVoting:
|
||||
"""E2E tests for review voting (helpful/not helpful)."""
|
||||
|
||||
def test__review__shows_vote_buttons(self, page: Page, live_server, parks_data):
|
||||
"""Test reviews show vote buttons."""
|
||||
park = parks_data[0]
|
||||
page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
reviews_tab = page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
# Look for helpful/upvote buttons
|
||||
vote_buttons = page.locator(
|
||||
"button:has-text('Helpful'), button[aria-label*='helpful'], .vote-button"
|
||||
)
|
||||
|
||||
if vote_buttons.count() > 0:
|
||||
expect(vote_buttons.first).to_be_visible()
|
||||
|
||||
def test__vote__authenticated__registers_vote(
|
||||
self, auth_page: Page, live_server, parks_data
|
||||
):
|
||||
"""Test authenticated user can vote on review."""
|
||||
park = parks_data[0]
|
||||
auth_page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
reviews_tab = auth_page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
helpful_button = auth_page.locator(
|
||||
"button:has-text('Helpful'), button[aria-label*='helpful']"
|
||||
)
|
||||
|
||||
if helpful_button.count() > 0:
|
||||
helpful_button.first.click()
|
||||
|
||||
# Button should show voted state
|
||||
auth_page.wait_for_timeout(500)
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestRideReviews:
|
||||
"""E2E tests for ride-specific reviews."""
|
||||
|
||||
def test__ride_page__shows_reviews(self, page: Page, live_server, rides_data):
|
||||
"""Test ride page shows reviews section."""
|
||||
ride = rides_data[0]
|
||||
page.goto(f"{live_server.url}/rides/{ride.slug}/")
|
||||
|
||||
# Reviews section should be present
|
||||
reviews_section = page.locator(
|
||||
"[data-testid='reviews'], #reviews, .reviews-section"
|
||||
)
|
||||
|
||||
if reviews_section.count() > 0:
|
||||
expect(reviews_section.first).to_be_visible()
|
||||
|
||||
def test__ride_review__includes_ride_experience_fields(
|
||||
self, auth_page: Page, live_server, rides_data
|
||||
):
|
||||
"""Test ride review form includes experience fields."""
|
||||
ride = rides_data[0]
|
||||
auth_page.goto(f"{live_server.url}/rides/{ride.slug}/")
|
||||
|
||||
write_review = auth_page.locator(
|
||||
"button:has-text('Write Review'), a:has-text('Write Review')"
|
||||
)
|
||||
|
||||
if write_review.count() > 0:
|
||||
write_review.first.click()
|
||||
|
||||
# Ride-specific fields
|
||||
intensity_field = auth_page.locator(
|
||||
"select[name='intensity'], input[name='intensity']"
|
||||
)
|
||||
wait_time_field = auth_page.locator(
|
||||
"input[name='wait_time'], select[name='wait_time']"
|
||||
)
|
||||
|
||||
# At least one experience field should be present
|
||||
if intensity_field.count() > 0:
|
||||
expect(intensity_field.first).to_be_visible()
|
||||
|
||||
|
||||
@pytest.mark.e2e
|
||||
class TestReviewFiltering:
|
||||
"""E2E tests for review filtering and sorting."""
|
||||
|
||||
def test__reviews__sort_by_date(self, page: Page, live_server, parks_data):
|
||||
"""Test reviews can be sorted by date."""
|
||||
park = parks_data[0]
|
||||
page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
reviews_tab = page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
sort_select = page.locator(
|
||||
"select[name='sort'], [data-testid='sort-reviews']"
|
||||
)
|
||||
|
||||
if sort_select.count() > 0:
|
||||
sort_select.first.select_option("date")
|
||||
page.wait_for_timeout(500)
|
||||
|
||||
def test__reviews__filter_by_rating(self, page: Page, live_server, parks_data):
|
||||
"""Test reviews can be filtered by rating."""
|
||||
park = parks_data[0]
|
||||
page.goto(f"{live_server.url}/parks/{park.slug}/")
|
||||
|
||||
reviews_tab = page.get_by_role("tab", name="Reviews")
|
||||
if reviews_tab.count() > 0:
|
||||
reviews_tab.click()
|
||||
|
||||
rating_filter = page.locator(
|
||||
"select[name='rating'], [data-testid='rating-filter']"
|
||||
)
|
||||
|
||||
if rating_filter.count() > 0:
|
||||
rating_filter.first.select_option("5")
|
||||
page.wait_for_timeout(500)
|
||||
Reference in New Issue
Block a user